fumadocs-epub 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { LoaderConfig, LoaderOutput } from "fumadocs-core/source";
2
-
3
2
  //#region src/types.d.ts
4
3
  interface EpubConfig<Config extends LoaderConfig = LoaderConfig> {
5
4
  /** Book title */
@@ -26,8 +25,8 @@ interface EpubConfig<Config extends LoaderConfig = LoaderConfig> {
26
25
  publicDir?: string;
27
26
  }
28
27
  interface EpubExportOptions<Config extends LoaderConfig = LoaderConfig> extends EpubConfig<Config> {
29
- /** Fumadocs source (from loader()) */
30
- source: LoaderOutput<Config>;
28
+ /** Fumadocs source (from `loader()`, or a function resolving one for runtime content sources) */
29
+ source: LoaderOutput<Config> | (() => Promise<LoaderOutput<Config>>);
31
30
  /** function to get page Markdown content */
32
31
  getMarkdown?: (page: Config['page']) => string | undefined | Promise<string | undefined>;
33
32
  }
@@ -36,9 +35,35 @@ interface EpubExportOptions<Config extends LoaderConfig = LoaderConfig> extends
36
35
  /**
37
36
  * Minimal, readable CSS stylesheet suitable for e-readers.
38
37
  */
39
- declare const defaultEpubStyles = "\nbody {\n font-family: Georgia, serif;\n font-size: 1em;\n line-height: 1.6;\n margin: 1em;\n}\n\nh1, h2, h3, h4, h5, h6 {\n font-family: Georgia, serif;\n margin-top: 1.5em;\n margin-bottom: 0.5em;\n page-break-after: avoid;\n}\n\nh1 { font-size: 1.5em; }\nh2 { font-size: 1.3em; }\nh3 { font-size: 1.15em; }\n\npre, code {\n font-family: \"Consolas\", \"Monaco\", \"Courier New\", monospace;\n font-size: 0.9em;\n}\n\npre {\n background: #f5f5f5;\n padding: 1em;\n overflow-x: auto;\n page-break-inside: avoid;\n}\n\ncode {\n background: #f5f5f5;\n padding: 0.2em 0.4em;\n}\n\npre code {\n background: none;\n padding: 0;\n}\n\nimg {\n max-width: 100%;\n height: auto;\n}\n\nblockquote {\n margin: 1em 0;\n padding-left: 1em;\n border-left: 4px solid #ccc;\n color: #555;\n}\n\ntable {\n border-collapse: collapse;\n width: 100%;\n page-break-inside: avoid;\n}\n\nth, td {\n border: 1px solid #ddd;\n padding: 0.5em;\n}\n\nth {\n background: #f5f5f5;\n}\n\np {\n margin: 0.75em 0;\n}\n\nul, ol {\n margin: 0.75em 0;\n padding-left: 1.5em;\n}\n\na {\n color: #0066cc;\n}\n";
38
+ export declare const defaultEpubStyles = "\nbody {\n font-family: Georgia, serif;\n font-size: 1em;\n line-height: 1.6;\n margin: 1em;\n}\n\nh1, h2, h3, h4, h5, h6 {\n font-family: Georgia, serif;\n margin-top: 1.5em;\n margin-bottom: 0.5em;\n page-break-after: avoid;\n}\n\nh1 { font-size: 1.5em; }\nh2 { font-size: 1.3em; }\nh3 { font-size: 1.15em; }\n\npre, code {\n font-family: \"Consolas\", \"Monaco\", \"Courier New\", monospace;\n font-size: 0.9em;\n}\n\npre {\n background: #f5f5f5;\n padding: 1em;\n overflow-x: auto;\n page-break-inside: avoid;\n}\n\ncode {\n background: #f5f5f5;\n padding: 0.2em 0.4em;\n}\n\npre code {\n background: none;\n padding: 0;\n}\n\nimg {\n max-width: 100%;\n height: auto;\n}\n\nblockquote {\n margin: 1em 0;\n padding-left: 1em;\n border-left: 4px solid #ccc;\n color: #555;\n}\n\ntable {\n border-collapse: collapse;\n width: 100%;\n page-break-inside: avoid;\n}\n\nth, td {\n border: 1px solid #ddd;\n padding: 0.5em;\n}\n\nth {\n background: #f5f5f5;\n}\n\np {\n margin: 0.75em 0;\n}\n\nul, ol {\n margin: 0.75em 0;\n padding-left: 1.5em;\n}\n\na {\n color: #0066cc;\n}\n";
40
39
  //#endregion
41
40
  //#region src/index.d.ts
41
+ export interface EpubExportAPI {
42
+ GET: (request: Request) => Promise<Response>;
43
+ }
44
+ /**
45
+ * A route handler that exports the docs as an EPUB file.
46
+ *
47
+ * The route is guarded by `secret`: it is rejected entirely when the secret is unset (e.g. a missing
48
+ * environment variable), otherwise the request must carry `Authorization: Bearer <secret>`.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * import { createEpubExportAPI } from 'fumadocs-epub';
53
+ * import { source } from '@/lib/source';
54
+ *
55
+ * export const { GET } = createEpubExportAPI({
56
+ * source,
57
+ * title: 'Documentation',
58
+ * secret: process.env.EXPORT_SECRET,
59
+ * });
60
+ * ```
61
+ */
62
+ export declare function createEpubExportAPI<C extends LoaderConfig>(options: EpubExportOptions<C> & {
63
+ secret: string | undefined;
64
+ /** @defaultValue 'docs.epub' */
65
+ filename?: string;
66
+ }): EpubExportAPI;
42
67
  /**
43
68
  * Export Fumadocs documentation to EPUB format.
44
69
  *
@@ -58,7 +83,7 @@ declare const defaultEpubStyles = "\nbody {\n font-family: Georgia, serif;\n f
58
83
  * });
59
84
  * ```
60
85
  */
61
- declare function exportEpub<C extends LoaderConfig>(options: EpubExportOptions<C>): Promise<Buffer>;
86
+ export declare function exportEpub<C extends LoaderConfig>(options: EpubExportOptions<C>): Promise<Buffer>;
62
87
  //#endregion
63
- export { type EpubConfig, type EpubExportOptions, defaultEpubStyles, exportEpub };
88
+ export type { EpubConfig, EpubExportOptions };
64
89
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/default-styles.ts","../src/index.ts"],"mappings":";;;UAEiB,UAAA,gBAA0B,YAAA,GAAe,YAAA;;EAExD,KAAA;EAFyB;EAIzB,MAAA;EAJyC;EAMzC,WAAA;EAUsB;EARtB,QAAA;EAU4B;EAR5B,SAAA;EAV0B;EAY1B,KAAA;EAZwD;EAcxD,UAAA;EAVA;EAYA,YAAA,IAAgB,IAAA,EAAM,MAAA;EARtB;EAUA,YAAA,IAAgB,IAAA,EAAM,MAAA;EANtB;EAQA,GAAA;EAJA;EAMA,SAAA;AAAA;AAAA,UAGe,iBAAA,gBACA,YAAA,GAAe,YAAA,UACtB,UAAA,CAAW,MAAA;EATG;EAWtB,MAAA,EAAQ,YAAA,CAAa,MAAA;EATrB;EAWA,WAAA,IAAe,IAAA,EAAM,MAAA,kCAAwC,OAAA;AAAA;;;;;;cC9BlD,iBAAA;;;;;;;;;;;;;;;;;;;;;;iBC4DS,UAAA,WAAqB,YAAA,EACzC,OAAA,EAAS,iBAAA,CAAkB,CAAA,IAC1B,OAAA,CAAQ,MAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/default-styles.ts","../src/index.ts"],"mappings":";;UAEiB,WAAW,eAAe,eAAe;;EAExD;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA,gBAAgB,MAAM;;EAEtB,gBAAgB,MAAM;;EAEtB;;EAEA;;UAGe,kBACf,eAAe,eAAe,sBACtB,WAAW;;EAEnB,QAAQ,aAAa,iBAAiB,QAAQ,aAAa;;EAE3D,eAAe,MAAM,wCAAwC;;;;;;;qBC9BlD;;;iBCSI;EACf,MAAM,SAAS,YAAY,QAAQ;;;;;;;;;;;;;;;;;;;;wBAqBrB,oBAAoB,UAAU,cAC5C,SAAS,kBAAkB;EACzB;;EAEA;IAED;;;;;;;;;;;;;;;;;;;;wBAyEmB,WAAW,UAAU,cACzC,SAAS,kBAAkB,KAC1B,QAAQ"}
package/dist/index.js CHANGED
@@ -159,6 +159,37 @@ a {
159
159
  //#endregion
160
160
  //#region src/index.ts
161
161
  /**
162
+ * A route handler that exports the docs as an EPUB file.
163
+ *
164
+ * The route is guarded by `secret`: it is rejected entirely when the secret is unset (e.g. a missing
165
+ * environment variable), otherwise the request must carry `Authorization: Bearer <secret>`.
166
+ *
167
+ * @example
168
+ * ```ts
169
+ * import { createEpubExportAPI } from 'fumadocs-epub';
170
+ * import { source } from '@/lib/source';
171
+ *
172
+ * export const { GET } = createEpubExportAPI({
173
+ * source,
174
+ * title: 'Documentation',
175
+ * secret: process.env.EXPORT_SECRET,
176
+ * });
177
+ * ```
178
+ */
179
+ function createEpubExportAPI(options) {
180
+ const { secret, filename = "docs.epub", ...rest } = options;
181
+ return { async GET(request) {
182
+ if (!secret) return new Response("the export secret is not configured.", { status: 503 });
183
+ const auth = request.headers.get("authorization");
184
+ if (auth?.replace(/^Bearer\s+/i, "") !== secret) return new Response("Unauthorized", { status: auth ? 403 : 401 });
185
+ const buffer = await exportEpub(rest);
186
+ return new Response(new Uint8Array(buffer), { headers: {
187
+ "Content-Type": "application/epub+zip",
188
+ "Content-Disposition": `attachment; filename="${filename}"`
189
+ } });
190
+ } };
191
+ }
192
+ /**
162
193
  * Get page directory for resolving relative image paths
163
194
  */
164
195
  function getPageDir(page, cwd) {
@@ -201,13 +232,14 @@ function pathToFileUrl(filePath) {
201
232
  */
202
233
  async function exportEpub(options) {
203
234
  const cwd = process.cwd();
204
- const { source, getMarkdown = async (page) => {
235
+ const { source: input, getMarkdown = async (page) => {
205
236
  try {
206
237
  return await page.data.getText("raw");
207
238
  } catch {
208
239
  console.warn(`Failed to get processed markdown for page "${page.absolutePath ?? page.path}", please specify the "getMarkdown" option.`);
209
240
  }
210
241
  }, title, author = "anonymous", description, language = "en", publisher = "anonymous", cover, outputPath, includePages, excludePages, css, publicDir = path.resolve("public") } = options;
242
+ const source = typeof input === "function" ? await input() : input;
211
243
  const orderedPages = getPagesInTreeOrder(source.getPageTree(), (node) => source.getNodePage?.(node)).filter((p) => p != null);
212
244
  const filteredPages = (orderedPages.length > 0 ? orderedPages : source.getPages()).filter((page) => {
213
245
  if (includePages && !includePages(page)) return false;
@@ -225,7 +257,7 @@ async function exportEpub(options) {
225
257
  content: html
226
258
  });
227
259
  }
228
- const buffer = await epub({
260
+ const epubOptions = {
229
261
  title,
230
262
  author: Array.isArray(author) ? author : [author],
231
263
  description,
@@ -236,10 +268,12 @@ async function exportEpub(options) {
236
268
  prependChapterTitles: true,
237
269
  numberChaptersInTOC: true,
238
270
  tocTitle: "Table of Contents"
239
- }, chapters.map((ch) => ({
271
+ };
272
+ const content = chapters.map((ch) => ({
240
273
  title: ch.title,
241
274
  content: ch.content
242
- })));
275
+ }));
276
+ const buffer = await epub(epubOptions, content);
243
277
  if (outputPath) {
244
278
  const fs = await import("node:fs/promises");
245
279
  await fs.mkdir(path.dirname(outputPath), { recursive: true });
@@ -248,6 +282,6 @@ async function exportEpub(options) {
248
282
  return buffer;
249
283
  }
250
284
  //#endregion
251
- export { defaultEpubStyles, exportEpub };
285
+ export { createEpubExportAPI, defaultEpubStyles, exportEpub };
252
286
 
253
287
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/toc-builder.ts","../src/image-resolver.ts","../src/markdown-to-html.ts","../src/default-styles.ts","../src/index.ts"],"sourcesContent":["import type * as PageTree from 'fumadocs-core/page-tree';\nimport { flattenTree } from 'fumadocs-core/page-tree';\n\n/**\n * Get pages in the order they appear in the page tree (navigation order).\n * Uses flattenTree to traverse the tree depth-first, resolving each Item to a page.\n */\nexport function getPagesInTreeOrder<Page>(\n pageTree: PageTree.Root,\n getNodePage: (node: PageTree.Item) => Page | undefined,\n): Page[] {\n const mainItems = flattenTree(pageTree.children);\n const fallbackItems = pageTree.fallback ? flattenTree(pageTree.fallback.children) : [];\n const items = [...mainItems, ...fallbackItems];\n const pages: Page[] = [];\n\n for (const item of items) {\n const page = getNodePage(item);\n if (page) {\n pages.push(page);\n }\n }\n\n return pages;\n}\n","import path from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport fs from 'node:fs';\n\n/**\n * Resolve image src to file:// URL for epub-gen-memory to embed.\n * Handles relative paths (resolved from page dir), absolute paths, and remote URLs.\n */\nexport function resolveImageSrc(\n src: string,\n pageDir: string,\n publicDir: string,\n): string | undefined {\n // Already a file:// URL\n if (src.startsWith('file://')) {\n return src;\n }\n\n // Remote URL - epub-gen-memory can fetch these\n if (src.startsWith('http://') || src.startsWith('https://')) {\n return src;\n }\n\n // Data URL - pass through\n if (src.startsWith('data:')) {\n return src;\n }\n\n // Protocol-relative URL (//cdn.example.com/...) - normalize to https:\n if (src.startsWith('//')) {\n return `https:${src}`;\n }\n\n // Absolute path starting with / - could be from public dir\n if (src.startsWith('/')) {\n const resolved = path.join(publicDir, src.slice(1));\n if (fs.existsSync(resolved)) return pathToFileURL(resolved).href;\n }\n // Relative path or others - resolve from page directory\n const resolved = path.resolve(pageDir, src);\n if (fs.existsSync(resolved)) return pathToFileURL(resolved).href;\n}\n","import type { Transformer } from 'unified';\nimport remarkGfm from 'remark-gfm';\nimport remarkRehype from 'remark-rehype';\nimport rehypeStringify from 'rehype-stringify';\nimport type { Root } from 'mdast';\nimport { visit } from 'unist-util-visit';\nimport { remark } from 'remark';\nimport { resolveImageSrc } from './image-resolver';\n\nconst processor = remark()\n .use(remarkGfm)\n .use(remarkResolveImg)\n .use(remarkRehype, { allowDangerousHtml: true })\n .use(rehypeStringify, { allowDangerousHtml: true });\n\ndeclare module 'vfile' {\n export interface DataMap {\n pageDir: string;\n publicDir: string;\n }\n}\n\nfunction remarkResolveImg(): Transformer<Root, Root> {\n return (tree, file) => {\n const { pageDir, publicDir } = file.data;\n if (!pageDir || !publicDir) return;\n\n visit(tree, 'image', (node, idx, parent) => {\n const resolved = resolveImageSrc(node.url, pageDir, publicDir);\n if (resolved) {\n node.url = resolved;\n } else if (idx !== undefined && parent) {\n parent.children.splice(idx, 1);\n }\n });\n };\n}\n\n/**\n * Convert markdown string to HTML suitable for EPUB chapters.\n * Uses unified pipeline: remark-parse -> remark-gfm -> remark-rehype -> rehype-stringify\n */\nexport async function markdownToHtml(\n markdown: string,\n pageDir: string,\n publicDir: string,\n): Promise<string> {\n const result = await processor.process({\n value: markdown,\n data: {\n pageDir,\n publicDir,\n },\n });\n return String(result);\n}\n","/**\n * Minimal, readable CSS stylesheet suitable for e-readers.\n */\nexport const defaultEpubStyles = `\nbody {\n font-family: Georgia, serif;\n font-size: 1em;\n line-height: 1.6;\n margin: 1em;\n}\n\nh1, h2, h3, h4, h5, h6 {\n font-family: Georgia, serif;\n margin-top: 1.5em;\n margin-bottom: 0.5em;\n page-break-after: avoid;\n}\n\nh1 { font-size: 1.5em; }\nh2 { font-size: 1.3em; }\nh3 { font-size: 1.15em; }\n\npre, code {\n font-family: \"Consolas\", \"Monaco\", \"Courier New\", monospace;\n font-size: 0.9em;\n}\n\npre {\n background: #f5f5f5;\n padding: 1em;\n overflow-x: auto;\n page-break-inside: avoid;\n}\n\ncode {\n background: #f5f5f5;\n padding: 0.2em 0.4em;\n}\n\npre code {\n background: none;\n padding: 0;\n}\n\nimg {\n max-width: 100%;\n height: auto;\n}\n\nblockquote {\n margin: 1em 0;\n padding-left: 1em;\n border-left: 4px solid #ccc;\n color: #555;\n}\n\ntable {\n border-collapse: collapse;\n width: 100%;\n page-break-inside: avoid;\n}\n\nth, td {\n border: 1px solid #ddd;\n padding: 0.5em;\n}\n\nth {\n background: #f5f5f5;\n}\n\np {\n margin: 0.75em 0;\n}\n\nul, ol {\n margin: 0.75em 0;\n padding-left: 1.5em;\n}\n\na {\n color: #0066cc;\n}\n`;\n","import epub, { type Options } from 'epub-gen-memory';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { LoaderConfig, Page } from 'fumadocs-core/source';\nimport { getPagesInTreeOrder } from './toc-builder';\nimport { markdownToHtml } from './markdown-to-html';\nimport { defaultEpubStyles } from './default-styles';\nimport type { EpubExportOptions } from './types';\n\nexport { defaultEpubStyles } from './default-styles';\nexport type { EpubConfig, EpubExportOptions } from './types';\n\n/**\n * Get page directory for resolving relative image paths\n */\nfunction getPageDir(page: Page, cwd: string): string {\n if (page.absolutePath) {\n return path.dirname(page.absolutePath);\n }\n return path.resolve(cwd, path.dirname(page.path));\n}\n\n/**\n * Resolve cover image path - support file://, http(s)://, /public/..., or relative\n */\nfunction resolveCoverPath(\n cover: string | undefined,\n cwd: string,\n publicDir?: string,\n): string | undefined {\n if (!cover) return undefined;\n if (cover.startsWith('file://') || cover.startsWith('http://') || cover.startsWith('https://'))\n return cover;\n if (cover.startsWith('/')) {\n const base = publicDir ?? path.join(cwd, 'public');\n return pathToFileUrl(path.join(base, cover.slice(1)));\n }\n return pathToFileUrl(path.resolve(cwd, cover));\n}\n\nfunction pathToFileUrl(filePath: string): string {\n return pathToFileURL(filePath).href;\n}\n\n/**\n * Export Fumadocs documentation to EPUB format.\n *\n * @param options - Export configuration with source and epub config\n * @returns Promise resolving to the EPUB file as a Buffer\n *\n * @example\n * ```ts\n * import { exportEpub } from 'fumadocs-epub';\n * import { source } from '@/lib/source';\n *\n * const buffer = await exportEpub({\n * source,\n * title: 'My Documentation',\n * author: 'My Team',\n * cover: '/cover.png',\n * });\n * ```\n */\nexport async function exportEpub<C extends LoaderConfig>(\n options: EpubExportOptions<C>,\n): Promise<Buffer> {\n const cwd = process.cwd();\n const {\n source,\n getMarkdown = async (page) => {\n try {\n return await (page.data as { getText: (type: string) => Promise<string> }).getText('raw');\n } catch {\n console.warn(\n `Failed to get processed markdown for page \"${page.absolutePath ?? page.path}\", please specify the \"getMarkdown\" option.`,\n );\n }\n },\n title,\n author = 'anonymous',\n description,\n language = 'en',\n publisher = 'anonymous',\n cover,\n outputPath,\n includePages,\n excludePages,\n css,\n publicDir = path.resolve('public'),\n } = options;\n\n // Get pages in tree order (navigation order)\n const pageTree = source.getPageTree();\n const orderedPages = getPagesInTreeOrder(pageTree, (node) => source.getNodePage?.(node)).filter(\n (p): p is Page => p != null,\n );\n\n // Fallback to getPages() if tree order yields no pages (e.g. i18n)\n const pages = orderedPages.length > 0 ? orderedPages : source.getPages();\n\n // Filter pages\n const filteredPages = pages.filter((page) => {\n if (includePages && !includePages(page)) return false;\n if (excludePages && excludePages(page)) return false;\n return true;\n });\n\n const chapters: { title: string; content: string }[] = [];\n\n for (const page of filteredPages) {\n const markdown = await getMarkdown(page);\n if (!markdown) continue;\n\n const pageDir = getPageDir(page, cwd);\n const html = await markdownToHtml(markdown, pageDir, publicDir);\n const pageTitle = page.data.title ?? page.slugs[page.slugs.length - 1] ?? 'Untitled';\n\n chapters.push({\n title: pageTitle,\n content: html,\n });\n }\n\n const epubOptions: Options = {\n title,\n author: Array.isArray(author) ? author : [author],\n description,\n lang: language,\n publisher,\n cover: resolveCoverPath(cover, cwd, publicDir) ?? cover,\n css: css ?? defaultEpubStyles,\n prependChapterTitles: true,\n numberChaptersInTOC: true,\n tocTitle: 'Table of Contents',\n };\n\n const content = chapters.map((ch) => ({\n title: ch.title,\n content: ch.content,\n }));\n\n const buffer = await epub(epubOptions, content);\n\n if (outputPath) {\n const fs = await import('node:fs/promises');\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.writeFile(outputPath, buffer);\n }\n return buffer;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/toc-builder.ts","../src/image-resolver.ts","../src/markdown-to-html.ts","../src/default-styles.ts","../src/index.ts"],"sourcesContent":["import type * as PageTree from 'fumadocs-core/page-tree';\nimport { flattenTree } from 'fumadocs-core/page-tree';\n\n/**\n * Get pages in the order they appear in the page tree (navigation order).\n * Uses flattenTree to traverse the tree depth-first, resolving each Item to a page.\n */\nexport function getPagesInTreeOrder<Page>(\n pageTree: PageTree.Root,\n getNodePage: (node: PageTree.Item) => Page | undefined,\n): Page[] {\n const mainItems = flattenTree(pageTree.children);\n const fallbackItems = pageTree.fallback ? flattenTree(pageTree.fallback.children) : [];\n const items = [...mainItems, ...fallbackItems];\n const pages: Page[] = [];\n\n for (const item of items) {\n const page = getNodePage(item);\n if (page) {\n pages.push(page);\n }\n }\n\n return pages;\n}\n","import path from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport fs from 'node:fs';\n\n/**\n * Resolve image src to file:// URL for epub-gen-memory to embed.\n * Handles relative paths (resolved from page dir), absolute paths, and remote URLs.\n */\nexport function resolveImageSrc(\n src: string,\n pageDir: string,\n publicDir: string,\n): string | undefined {\n // Already a file:// URL\n if (src.startsWith('file://')) {\n return src;\n }\n\n // Remote URL - epub-gen-memory can fetch these\n if (src.startsWith('http://') || src.startsWith('https://')) {\n return src;\n }\n\n // Data URL - pass through\n if (src.startsWith('data:')) {\n return src;\n }\n\n // Protocol-relative URL (//cdn.example.com/...) - normalize to https:\n if (src.startsWith('//')) {\n return `https:${src}`;\n }\n\n // Absolute path starting with / - could be from public dir\n if (src.startsWith('/')) {\n const resolved = path.join(publicDir, src.slice(1));\n if (fs.existsSync(resolved)) return pathToFileURL(resolved).href;\n }\n // Relative path or others - resolve from page directory\n const resolved = path.resolve(pageDir, src);\n if (fs.existsSync(resolved)) return pathToFileURL(resolved).href;\n}\n","import type { Transformer } from 'unified';\nimport remarkGfm from 'remark-gfm';\nimport remarkRehype from 'remark-rehype';\nimport rehypeStringify from 'rehype-stringify';\nimport type { Root } from 'mdast';\nimport { visit } from 'unist-util-visit';\nimport { remark } from 'remark';\nimport { resolveImageSrc } from './image-resolver';\n\nconst processor = remark()\n .use(remarkGfm)\n .use(remarkResolveImg)\n .use(remarkRehype, { allowDangerousHtml: true })\n .use(rehypeStringify, { allowDangerousHtml: true });\n\ndeclare module 'vfile' {\n export interface DataMap {\n pageDir: string;\n publicDir: string;\n }\n}\n\nfunction remarkResolveImg(): Transformer<Root, Root> {\n return (tree, file) => {\n const { pageDir, publicDir } = file.data;\n if (!pageDir || !publicDir) return;\n\n visit(tree, 'image', (node, idx, parent) => {\n const resolved = resolveImageSrc(node.url, pageDir, publicDir);\n if (resolved) {\n node.url = resolved;\n } else if (idx !== undefined && parent) {\n parent.children.splice(idx, 1);\n }\n });\n };\n}\n\n/**\n * Convert markdown string to HTML suitable for EPUB chapters.\n * Uses unified pipeline: remark-parse -> remark-gfm -> remark-rehype -> rehype-stringify\n */\nexport async function markdownToHtml(\n markdown: string,\n pageDir: string,\n publicDir: string,\n): Promise<string> {\n const result = await processor.process({\n value: markdown,\n data: {\n pageDir,\n publicDir,\n },\n });\n return String(result);\n}\n","/**\n * Minimal, readable CSS stylesheet suitable for e-readers.\n */\nexport const defaultEpubStyles = `\nbody {\n font-family: Georgia, serif;\n font-size: 1em;\n line-height: 1.6;\n margin: 1em;\n}\n\nh1, h2, h3, h4, h5, h6 {\n font-family: Georgia, serif;\n margin-top: 1.5em;\n margin-bottom: 0.5em;\n page-break-after: avoid;\n}\n\nh1 { font-size: 1.5em; }\nh2 { font-size: 1.3em; }\nh3 { font-size: 1.15em; }\n\npre, code {\n font-family: \"Consolas\", \"Monaco\", \"Courier New\", monospace;\n font-size: 0.9em;\n}\n\npre {\n background: #f5f5f5;\n padding: 1em;\n overflow-x: auto;\n page-break-inside: avoid;\n}\n\ncode {\n background: #f5f5f5;\n padding: 0.2em 0.4em;\n}\n\npre code {\n background: none;\n padding: 0;\n}\n\nimg {\n max-width: 100%;\n height: auto;\n}\n\nblockquote {\n margin: 1em 0;\n padding-left: 1em;\n border-left: 4px solid #ccc;\n color: #555;\n}\n\ntable {\n border-collapse: collapse;\n width: 100%;\n page-break-inside: avoid;\n}\n\nth, td {\n border: 1px solid #ddd;\n padding: 0.5em;\n}\n\nth {\n background: #f5f5f5;\n}\n\np {\n margin: 0.75em 0;\n}\n\nul, ol {\n margin: 0.75em 0;\n padding-left: 1.5em;\n}\n\na {\n color: #0066cc;\n}\n`;\n","import epub, { type Options } from 'epub-gen-memory';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { LoaderConfig, Page } from 'fumadocs-core/source';\nimport { getPagesInTreeOrder } from './toc-builder';\nimport { markdownToHtml } from './markdown-to-html';\nimport { defaultEpubStyles } from './default-styles';\nimport type { EpubExportOptions } from './types';\n\nexport { defaultEpubStyles } from './default-styles';\nexport type { EpubConfig, EpubExportOptions } from './types';\n\nexport interface EpubExportAPI {\n GET: (request: Request) => Promise<Response>;\n}\n\n/**\n * A route handler that exports the docs as an EPUB file.\n *\n * The route is guarded by `secret`: it is rejected entirely when the secret is unset (e.g. a missing\n * environment variable), otherwise the request must carry `Authorization: Bearer <secret>`.\n *\n * @example\n * ```ts\n * import { createEpubExportAPI } from 'fumadocs-epub';\n * import { source } from '@/lib/source';\n *\n * export const { GET } = createEpubExportAPI({\n * source,\n * title: 'Documentation',\n * secret: process.env.EXPORT_SECRET,\n * });\n * ```\n */\nexport function createEpubExportAPI<C extends LoaderConfig>(\n options: EpubExportOptions<C> & {\n secret: string | undefined;\n /** @defaultValue 'docs.epub' */\n filename?: string;\n },\n): EpubExportAPI {\n const { secret, filename = 'docs.epub', ...rest } = options;\n\n return {\n async GET(request) {\n if (!secret) return new Response('the export secret is not configured.', { status: 503 });\n const auth = request.headers.get('authorization');\n if (auth?.replace(/^Bearer\\s+/i, '') !== secret)\n return new Response('Unauthorized', { status: auth ? 403 : 401 });\n\n const buffer = await exportEpub(rest as EpubExportOptions<C>);\n\n return new Response(new Uint8Array(buffer), {\n headers: {\n 'Content-Type': 'application/epub+zip',\n 'Content-Disposition': `attachment; filename=\"${filename}\"`,\n },\n });\n },\n };\n}\n\n/**\n * Get page directory for resolving relative image paths\n */\nfunction getPageDir(page: Page, cwd: string): string {\n if (page.absolutePath) {\n return path.dirname(page.absolutePath);\n }\n return path.resolve(cwd, path.dirname(page.path));\n}\n\n/**\n * Resolve cover image path - support file://, http(s)://, /public/..., or relative\n */\nfunction resolveCoverPath(\n cover: string | undefined,\n cwd: string,\n publicDir?: string,\n): string | undefined {\n if (!cover) return undefined;\n if (cover.startsWith('file://') || cover.startsWith('http://') || cover.startsWith('https://'))\n return cover;\n if (cover.startsWith('/')) {\n const base = publicDir ?? path.join(cwd, 'public');\n return pathToFileUrl(path.join(base, cover.slice(1)));\n }\n return pathToFileUrl(path.resolve(cwd, cover));\n}\n\nfunction pathToFileUrl(filePath: string): string {\n return pathToFileURL(filePath).href;\n}\n\n/**\n * Export Fumadocs documentation to EPUB format.\n *\n * @param options - Export configuration with source and epub config\n * @returns Promise resolving to the EPUB file as a Buffer\n *\n * @example\n * ```ts\n * import { exportEpub } from 'fumadocs-epub';\n * import { source } from '@/lib/source';\n *\n * const buffer = await exportEpub({\n * source,\n * title: 'My Documentation',\n * author: 'My Team',\n * cover: '/cover.png',\n * });\n * ```\n */\nexport async function exportEpub<C extends LoaderConfig>(\n options: EpubExportOptions<C>,\n): Promise<Buffer> {\n const cwd = process.cwd();\n const {\n source: input,\n getMarkdown = async (page) => {\n try {\n return await (page.data as { getText: (type: string) => Promise<string> }).getText('raw');\n } catch {\n console.warn(\n `Failed to get processed markdown for page \"${page.absolutePath ?? page.path}\", please specify the \"getMarkdown\" option.`,\n );\n }\n },\n title,\n author = 'anonymous',\n description,\n language = 'en',\n publisher = 'anonymous',\n cover,\n outputPath,\n includePages,\n excludePages,\n css,\n publicDir = path.resolve('public'),\n } = options;\n\n const source = typeof input === 'function' ? await input() : input;\n // Get pages in tree order (navigation order)\n const pageTree = source.getPageTree();\n const orderedPages = getPagesInTreeOrder(pageTree, (node) => source.getNodePage?.(node)).filter(\n (p): p is Page => p != null,\n );\n\n // Fallback to getPages() if tree order yields no pages (e.g. i18n)\n const pages = orderedPages.length > 0 ? orderedPages : source.getPages();\n\n // Filter pages\n const filteredPages = pages.filter((page) => {\n if (includePages && !includePages(page)) return false;\n if (excludePages && excludePages(page)) return false;\n return true;\n });\n\n const chapters: { title: string; content: string }[] = [];\n\n for (const page of filteredPages) {\n const markdown = await getMarkdown(page);\n if (!markdown) continue;\n\n const pageDir = getPageDir(page, cwd);\n const html = await markdownToHtml(markdown, pageDir, publicDir);\n const pageTitle = page.data.title ?? page.slugs[page.slugs.length - 1] ?? 'Untitled';\n\n chapters.push({\n title: pageTitle,\n content: html,\n });\n }\n\n const epubOptions: Options = {\n title,\n author: Array.isArray(author) ? author : [author],\n description,\n lang: language,\n publisher,\n cover: resolveCoverPath(cover, cwd, publicDir) ?? cover,\n css: css ?? defaultEpubStyles,\n prependChapterTitles: true,\n numberChaptersInTOC: true,\n tocTitle: 'Table of Contents',\n };\n\n const content = chapters.map((ch) => ({\n title: ch.title,\n content: ch.content,\n }));\n\n const buffer = await epub(epubOptions, content);\n\n if (outputPath) {\n const fs = await import('node:fs/promises');\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.writeFile(outputPath, buffer);\n }\n return buffer;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-epub",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "EPUB export for Fumadocs documentation",
5
5
  "keywords": [
6
6
  "documentation",
@@ -19,6 +19,7 @@
19
19
  "dist"
20
20
  ],
21
21
  "type": "module",
22
+ "sideEffects": false,
22
23
  "main": "./dist/index.js",
23
24
  "types": "./dist/index.d.ts",
24
25
  "exports": {
@@ -41,12 +42,12 @@
41
42
  },
42
43
  "devDependencies": {
43
44
  "@types/mdast": "^4.0.4",
44
- "@types/node": "26.1.0",
45
+ "@types/node": "26.5.0",
45
46
  "rimraf": "^6.0.0",
46
- "tsdown": "0.22.3",
47
- "typescript": "^6.0.3",
47
+ "tsdown": "0.23.0",
48
+ "typescript": "^7.0.2",
48
49
  "vfile": "^6.0.3",
49
- "fumadocs-core": "16.11.0",
50
+ "fumadocs-core": "16.15.11",
50
51
  "tsconfig": "0.0.0"
51
52
  },
52
53
  "peerDependencies": {