fumapress 0.2.5 → 0.3.1

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.
Files changed (44) hide show
  1. package/css/generated.css +155 -8
  2. package/dist/_virtual/_rolldown/runtime.js +24 -0
  3. package/dist/adapters/mdx/schema.d.ts +39 -0
  4. package/dist/adapters/mdx/schema.js +9 -0
  5. package/dist/adapters/mdx.js +25 -5
  6. package/dist/adapters/openapi.d.ts +15 -0
  7. package/dist/adapters/openapi.js +22 -0
  8. package/dist/components/blog-panel.js +56 -0
  9. package/dist/components/blog.js +60 -0
  10. package/dist/config.d.ts +23 -10
  11. package/dist/index.d.ts +2 -2
  12. package/dist/layouts/blog.d.ts +21 -0
  13. package/dist/layouts/blog.index.d.ts +15 -0
  14. package/dist/layouts/blog.index.js +36 -0
  15. package/dist/layouts/blog.js +82 -0
  16. package/dist/layouts/blog.tags.d.ts +19 -0
  17. package/dist/layouts/blog.tags.js +113 -0
  18. package/dist/layouts/docs.d.ts +3 -3
  19. package/dist/layouts/docs.js +24 -51
  20. package/dist/layouts/home.d.ts +18 -6
  21. package/dist/layouts/home.js +37 -21
  22. package/dist/layouts/notebook.d.ts +3 -3
  23. package/dist/layouts/notebook.js +24 -51
  24. package/dist/layouts/root.js +5 -6
  25. package/dist/layouts/switch.d.ts +11 -0
  26. package/dist/layouts/switch.js +21 -0
  27. package/dist/lib/cn.js +2 -0
  28. package/dist/lib/join-pathname.js +9 -0
  29. package/dist/lib/shared/blog.js +39 -0
  30. package/dist/lib/shared.d.ts +3 -2
  31. package/dist/lib/shared.js +50 -11
  32. package/dist/lib/types.d.ts +20 -6
  33. package/dist/node_modules/.pnpm/@fastify_deepmerge@3.2.1/node_modules/@fastify/deepmerge/index.js +108 -0
  34. package/dist/plugins/blog.d.ts +72 -0
  35. package/dist/plugins/blog.js +175 -0
  36. package/dist/plugins/llms.txt.d.ts +1 -1
  37. package/dist/plugins/llms.txt.js +5 -5
  38. package/dist/plugins/openapi.d.ts +13 -0
  39. package/dist/plugins/openapi.js +21 -0
  40. package/dist/router.d.ts +7 -6
  41. package/dist/router.js +49 -40
  42. package/dist/vite.d.ts +11 -3
  43. package/dist/vite.js +3 -4
  44. package/package.json +32 -11
@@ -0,0 +1,21 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { unstable_notFound } from "waku/router/server";
3
+ //#region src/layouts/switch.tsx
4
+ function createLayoutSwitchAuto(layouts) {
5
+ return async function(props) {
6
+ const { page } = props;
7
+ if (!page.type) unstable_notFound();
8
+ const Layout = layouts[page.type];
9
+ if (!Layout) unstable_notFound();
10
+ return /* @__PURE__ */ jsx(Layout, { ...props });
11
+ };
12
+ }
13
+ function createLayoutSwitch(detector, layouts) {
14
+ return async function(props) {
15
+ const Layout = layouts[detector.call(props.ctx, props.page)];
16
+ if (!Layout) unstable_notFound();
17
+ return /* @__PURE__ */ jsx(Layout, { ...props });
18
+ };
19
+ }
20
+ //#endregion
21
+ export { createLayoutSwitch, createLayoutSwitchAuto };
package/dist/lib/cn.js ADDED
@@ -0,0 +1,2 @@
1
+ import { twMerge as cn } from "tailwind-merge";
2
+ export { cn };
@@ -0,0 +1,9 @@
1
+ //#region src/lib/join-pathname.ts
2
+ function joinPathname(...paths) {
3
+ const segs = paths.join("/").split(/\/+/);
4
+ if (segs.length > 0 && segs[0].length === 0) segs.shift();
5
+ if (segs.length > 0 && segs[segs.length - 1].length === 0) segs.pop();
6
+ return "/" + segs.join("/");
7
+ }
8
+ //#endregion
9
+ export { joinPathname };
@@ -0,0 +1,39 @@
1
+ //#region src/lib/shared/blog.ts
2
+ async function getTags(ctx, page) {
3
+ for (const adapter of ctx.adapters) {
4
+ const tags = await adapter["blog:get-tags"]?.call(ctx, page);
5
+ if (tags !== void 0) return tags;
6
+ }
7
+ }
8
+ async function groupTagsI18n(ctx, blogPosts) {
9
+ const localeToTags = /* @__PURE__ */ new Map();
10
+ for (const page of blogPosts) {
11
+ const tags = await getTags(ctx, page);
12
+ if (!tags) continue;
13
+ const locale = page.locale ?? "";
14
+ let map = localeToTags.get(locale);
15
+ if (!map) {
16
+ map = /* @__PURE__ */ new Map();
17
+ localeToTags.set(locale, map);
18
+ }
19
+ for (const tag of tags) {
20
+ const count = map.get(tag) ?? 0;
21
+ map.set(tag, count + 1);
22
+ }
23
+ }
24
+ return localeToTags;
25
+ }
26
+ async function groupTags(ctx, blogPosts) {
27
+ const map = /* @__PURE__ */ new Map();
28
+ for (const page of blogPosts) {
29
+ const tags = await getTags(ctx, page);
30
+ if (!tags) continue;
31
+ for (const tag of tags) {
32
+ const count = map.get(tag) ?? 0;
33
+ map.set(tag, count + 1);
34
+ }
35
+ }
36
+ return map;
37
+ }
38
+ //#endregion
39
+ export { getTags, groupTags, groupTagsI18n };
@@ -1,7 +1,7 @@
1
1
  import { DocsLayoutContextData } from "../layouts/docs.js";
2
2
  import { HomeLayoutContextData } from "../layouts/home.js";
3
3
  import { NotebookLayoutContextData } from "../layouts/notebook.js";
4
- import { BuildMode, Config, ConfigContext, I18nConfig } from "../config.js";
4
+ import { BuildMode, ConfigContext, I18nConfig, Layouts, MetaConfig } from "../config.js";
5
5
  import { Adapter, Awaitable, ServerPlugin } from "./types.js";
6
6
  import { ComponentType, ReactNode } from "react";
7
7
  import { RootProviderProps } from "fumadocs-ui/provider/waku";
@@ -13,6 +13,7 @@ interface AppContext<C extends ConfigContext = ConfigContext> {
13
13
  getLoader: () => Awaitable<LoaderOutput<C["loaderConfig"]>>;
14
14
  plugins: ServerPlugin<C>[];
15
15
  adapters: Adapter<C>[];
16
+ layouts: Partial<Layouts<C>>;
16
17
  /** always `undefined`, easier way to infer types */
17
18
  $context: C;
18
19
  /**
@@ -20,7 +21,7 @@ interface AppContext<C extends ConfigContext = ConfigContext> {
20
21
  */
21
22
  data: AppContextData & Record<string, unknown>;
22
23
  i18nConfig?: I18nConfig<C["lang"]>;
23
- metaConfig?: Config<C>["meta"];
24
+ metaConfig?: MetaConfig<C>;
24
25
  siteConfig: {
25
26
  name: string;
26
27
  baseUrl?: string;
@@ -1,15 +1,19 @@
1
+ import { __toESM } from "../_virtual/_rolldown/runtime.js";
1
2
  import { getGitRootDir } from "./fs.js";
2
3
  import { fumadocsMdx } from "../adapters/mdx.js";
4
+ import { require_deepmerge } from "../node_modules/.pnpm/@fastify_deepmerge@3.2.1/node_modules/@fastify/deepmerge/index.js";
3
5
  import path from "node:path";
4
- import { Fragment } from "react";
6
+ import { Fragment, isValidElement } from "react";
5
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
6
8
  //#region src/lib/shared.tsx
9
+ var import_deepmerge = /* @__PURE__ */ __toESM(require_deepmerge());
7
10
  function parseConfig(config) {
8
11
  return {
9
12
  getLoader() {
10
13
  if (typeof config.loader === "function") return config.loader();
11
14
  return config.loader;
12
15
  },
16
+ layouts: config.layouts ?? {},
13
17
  plugins: config.plugins ?? [],
14
18
  adapters: config.adapters ?? [fumadocsMdx()],
15
19
  $context: void 0,
@@ -52,19 +56,54 @@ function getGitHubFileUrl(ctx, absolutePath) {
52
56
  if (p.startsWith("../")) return;
53
57
  return `https://github.com/${git.user}/${git.repo}/blob/${git.branch}/${p}`;
54
58
  }
55
- function baseOptions(ctx) {
59
+ function baseLayoutProps(ctx) {
56
60
  const { name, git } = ctx.siteConfig;
57
61
  return {
58
- nav: { title: name },
59
- githubUrl: git ? `https://github.com/${git.user}/${git.repo}` : void 0
62
+ githubUrl: git ? `https://github.com/${git.user}/${git.repo}` : void 0,
63
+ nav: { title: name }
60
64
  };
61
65
  }
62
- function TransformChildrenSlot({ Comp, props, children }) {
63
- if (props.children) for (const transformer of props.children) children = transformer(children);
64
- return /* @__PURE__ */ jsx(Comp, {
65
- ...props,
66
- children
67
- });
66
+ function createTransformChildren(Component) {
67
+ return function({ props, children }) {
68
+ if (props.children) for (const transformer of props.children) children = transformer(children);
69
+ return /* @__PURE__ */ jsx(Component, {
70
+ ...props,
71
+ children
72
+ });
73
+ };
74
+ }
75
+ async function renderBody(ctx, page, errorMessage) {
76
+ for (const adapter of ctx.adapters) {
77
+ const body = await adapter["core:render-body"]?.call(ctx, page);
78
+ if (body !== void 0) return body;
79
+ }
80
+ throw new Error(errorMessage);
81
+ }
82
+ async function renderToc(ctx, page) {
83
+ for (const adapter of ctx.adapters) {
84
+ const toc = await adapter["core:render-toc"]?.call(ctx, page);
85
+ if (toc !== void 0) return toc;
86
+ }
87
+ }
88
+ async function getCreationDate(ctx, page) {
89
+ for (const adapter of ctx.adapters) {
90
+ const date = await adapter["core:get-creation-date"]?.call(ctx, page);
91
+ if (date !== void 0) return date;
92
+ }
93
+ }
94
+ async function getLastModifiedDate(ctx, page) {
95
+ for (const adapter of ctx.adapters) {
96
+ const date = await adapter["core:get-modified-date"]?.call(ctx, page);
97
+ if (date !== void 0) return date;
98
+ }
68
99
  }
100
+ const mergeLayoutConfigs = (0, import_deepmerge.default)({
101
+ all: true,
102
+ onlyDefinedProperties: true,
103
+ isMergeableObject(value) {
104
+ if (isValidElement(value)) return false;
105
+ return import_deepmerge.default.isMergeableObject(value);
106
+ }
107
+ });
69
108
  //#endregion
70
- export { TransformChildrenSlot, baseOptions, getGitHubFileUrl, parseConfig, renderPageMeta, renderRootMeta };
109
+ export { baseLayoutProps, createTransformChildren, getCreationDate, getGitHubFileUrl, getLastModifiedDate, mergeLayoutConfigs, parseConfig, renderBody, renderPageMeta, renderRootMeta, renderToc };
@@ -1,9 +1,9 @@
1
1
  import { AppContext } from "./shared.js";
2
2
  import { ConfigContext } from "../config.js";
3
- import { createPages } from "waku";
4
3
  import { ReactNode } from "react";
5
- import { StructuredData } from "fumadocs-core/mdx-plugins";
4
+ import { CreateApi, CreateLayout, CreatePage, CreateRoot, CreateSlice } from "waku/router/server";
6
5
  import { TOCItemType } from "fumadocs-core/toc";
6
+ import { StructuredData } from "fumadocs-core/mdx-plugins";
7
7
 
8
8
  //#region src/lib/types.d.ts
9
9
  type Awaitable<T> = T | Promise<T>;
@@ -13,13 +13,27 @@ interface Adapter<C extends ConfigContext = ConfigContext> {
13
13
  "core:get-structured-data"?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<StructuredData | undefined>;
14
14
  "core:render-body"?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<ReactNode>;
15
15
  "core:render-toc"?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<TOCItemType[] | undefined>;
16
+ "core:get-creation-date"?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<Date | undefined>;
17
+ "core:get-modified-date"?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<Date | undefined>;
18
+ "blog:get-tags"?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<string[] | undefined>;
19
+ }
20
+ interface CreatePagesContext<C extends ConfigContext = ConfigContext> extends AppContext<C> {
21
+ /** call this function if the page's route is already created by your plugin */
22
+ markResolved: (page: C["loaderConfig"]["page"]) => void;
16
23
  }
17
24
  interface ServerPlugin<C extends ConfigContext = ConfigContext> {
18
25
  /** receive & modify context */
19
- init?: (this: AppContext<C>) => void;
20
- createPages?: (this: AppContext<C>, fns: RouteFns) => Awaitable<void>;
26
+ init?: (this: AppContext<C>) => Awaitable<void>;
27
+ createPages?: (this: CreatePagesContext<C>, fns: RouteFns) => Awaitable<void>;
21
28
  }
22
- type RouteFns = Parameters<Parameters<typeof createPages>[0]>[0] & {
29
+ interface BaseRouteFns {
30
+ createPage: CreatePage;
31
+ createLayout: CreateLayout;
32
+ createRoot: CreateRoot;
33
+ createApi: CreateApi;
34
+ createSlice: CreateSlice;
35
+ }
36
+ interface RouteFns extends BaseRouteFns {
23
37
  createApiIsomorphic: (config: {
24
38
  render: "static" | "dynamic";
25
39
  path: string;
@@ -28,6 +42,6 @@ type RouteFns = Parameters<Parameters<typeof createPages>[0]>[0] & {
28
42
  params: Record<string, string | string[]>;
29
43
  }) => Promise<Response>;
30
44
  }) => void;
31
- };
45
+ }
32
46
  //#endregion
33
47
  export { Adapter, Awaitable, RouteFns, ServerPlugin };
@@ -0,0 +1,108 @@
1
+ import { __commonJSMin } from "../../../../../../_virtual/_rolldown/runtime.js";
2
+ //#region ../../node_modules/.pnpm/@fastify+deepmerge@3.2.1/node_modules/@fastify/deepmerge/index.js
3
+ var require_deepmerge = /* @__PURE__ */ __commonJSMin(((exports, module) => {
4
+ const JSON_PROTO = Object.getPrototypeOf({});
5
+ function defaultIsMergeableObjectFactory() {
6
+ return function defaultIsMergeableObject(value) {
7
+ return typeof value === "object" && value !== null && !(value instanceof RegExp) && !(value instanceof Date);
8
+ };
9
+ }
10
+ function deepmergeConstructor(options) {
11
+ function isNotPrototypeKey(value) {
12
+ return value !== "constructor" && value !== "prototype" && value !== "__proto__";
13
+ }
14
+ function cloneArray(value) {
15
+ let i = 0;
16
+ const il = value.length;
17
+ const result = new Array(il);
18
+ for (; i < il; ++i) result[i] = clone(value[i]);
19
+ return result;
20
+ }
21
+ function cloneObject(target) {
22
+ const result = {};
23
+ if (cloneProtoObject && Object.getPrototypeOf(target) !== JSON_PROTO) return cloneProtoObject(target);
24
+ const targetKeys = getKeys(target);
25
+ let i, il, key;
26
+ for (i = 0, il = targetKeys.length; i < il; ++i) isNotPrototypeKey(key = targetKeys[i]) && (result[key] = clone(target[key]));
27
+ return result;
28
+ }
29
+ function concatArrays(target, source) {
30
+ const tl = target.length;
31
+ const sl = source.length;
32
+ let i = 0;
33
+ const result = new Array(tl + sl);
34
+ for (; i < tl; ++i) result[i] = clone(target[i]);
35
+ for (i = 0; i < sl; ++i) result[i + tl] = clone(source[i]);
36
+ return result;
37
+ }
38
+ const propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
39
+ function getSymbolsAndKeys(value) {
40
+ const result = Object.keys(value);
41
+ const keys = Object.getOwnPropertySymbols(value);
42
+ for (let i = 0, il = keys.length; i < il; ++i) propertyIsEnumerable.call(value, keys[i]) && result.push(keys[i]);
43
+ return result;
44
+ }
45
+ const getKeys = options?.symbols ? getSymbolsAndKeys : Object.keys;
46
+ const cloneProtoObject = typeof options?.cloneProtoObject === "function" ? options.cloneProtoObject : void 0;
47
+ const isMergeableObject = typeof options?.isMergeableObject === "function" ? options.isMergeableObject : defaultIsMergeableObjectFactory();
48
+ const onlyDefinedProperties = options?.onlyDefinedProperties === true;
49
+ function isPrimitive(value) {
50
+ return typeof value !== "object" || value === null;
51
+ }
52
+ const mergeArray = options && typeof options.mergeArray === "function" ? options.mergeArray({
53
+ clone,
54
+ deepmerge: _deepmerge,
55
+ getKeys,
56
+ isMergeableObject
57
+ }) : concatArrays;
58
+ function clone(entry) {
59
+ return isMergeableObject(entry) ? Array.isArray(entry) ? cloneArray(entry) : cloneObject(entry) : entry;
60
+ }
61
+ function mergeObject(target, source) {
62
+ const result = {};
63
+ const targetKeys = getKeys(target);
64
+ const sourceKeys = getKeys(source);
65
+ let i, il, key;
66
+ for (i = 0, il = targetKeys.length; i < il; ++i) isNotPrototypeKey(key = targetKeys[i]) && sourceKeys.indexOf(key) === -1 && (result[key] = clone(target[key]));
67
+ for (i = 0, il = sourceKeys.length; i < il; ++i) {
68
+ if (!isNotPrototypeKey(key = sourceKeys[i])) continue;
69
+ if (key in target) {
70
+ if (targetKeys.indexOf(key) !== -1) if (cloneProtoObject && isMergeableObject(source[key]) && Object.getPrototypeOf(source[key]) !== JSON_PROTO) result[key] = cloneProtoObject(source[key]);
71
+ else result[key] = _deepmerge(target[key], source[key]);
72
+ } else {
73
+ if (onlyDefinedProperties && typeof source[key] === "undefined") continue;
74
+ result[key] = clone(source[key]);
75
+ }
76
+ }
77
+ return result;
78
+ }
79
+ function _deepmerge(target, source) {
80
+ if (onlyDefinedProperties && typeof source === "undefined") return clone(target);
81
+ const sourceIsArray = Array.isArray(source);
82
+ const targetIsArray = Array.isArray(target);
83
+ if (isPrimitive(source)) return source;
84
+ else if (!isMergeableObject(target)) return clone(source);
85
+ else if (sourceIsArray && targetIsArray) return mergeArray(target, source);
86
+ else if (sourceIsArray !== targetIsArray) return clone(source);
87
+ else return mergeObject(target, source);
88
+ }
89
+ function _deepmergeAll() {
90
+ switch (arguments.length) {
91
+ case 0: return {};
92
+ case 1: return clone(arguments[0]);
93
+ case 2: return _deepmerge(arguments[0], arguments[1]);
94
+ }
95
+ let result;
96
+ for (let i = 0, il = arguments.length; i < il; ++i) result = _deepmerge(result, arguments[i]);
97
+ return result;
98
+ }
99
+ return options?.all ? _deepmergeAll : _deepmerge;
100
+ }
101
+ module.exports = deepmergeConstructor;
102
+ module.exports.default = deepmergeConstructor;
103
+ module.exports.deepmerge = deepmergeConstructor;
104
+ Object.defineProperty(module.exports, "isMergeableObject", { get: defaultIsMergeableObjectFactory });
105
+ }));
106
+ //#endregion
107
+ export default require_deepmerge();
108
+ export { require_deepmerge };
@@ -0,0 +1,72 @@
1
+ import { AppContext } from "../lib/shared.js";
2
+ import { ConfigContext } from "../config.js";
3
+ import { ServerPlugin } from "../lib/types.js";
4
+ import { ComponentType, ReactNode } from "react";
5
+
6
+ //#region src/plugins/blog.d.ts
7
+ interface BlogPluginOptions<C extends ConfigContext = ConfigContext> {
8
+ /** default to checking from `page.type` */
9
+ isBlog?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => boolean;
10
+ paths?: {
11
+ /**
12
+ * pathname for index page
13
+ *
14
+ * @default "/blog"
15
+ */
16
+ index?: string | false;
17
+ /**
18
+ * pathname for tags page
19
+ *
20
+ * @default "/blog/tags"
21
+ */
22
+ tags?: string | false;
23
+ };
24
+ layouts?: {
25
+ /** shared layout for blog */layout?: BlogLayout<C>; /** renderer of blog posts (displayed inside `layout`) */
26
+ page?: BlogLayoutPage<C>; /** renderer of index page (displayed inside `layout`) */
27
+ index?: BlogIndexPage<C>; /** renderer of tags page (displayed inside `layout`) */
28
+ tags?: BlogTagsPage<C>; /** renderer of tag page (displayed inside `layout`) */
29
+ tag?: BlogTagPage<C>;
30
+ };
31
+ }
32
+ interface BlogContext<C extends ConfigContext> {
33
+ indexPath: string | false;
34
+ tagsPath: string | false;
35
+ isBlog: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => boolean;
36
+ }
37
+ type BlogLayoutPage<C extends ConfigContext = ConfigContext> = ComponentType<{
38
+ lang?: string;
39
+ slugs: string[];
40
+ page: C["loaderConfig"]["page"];
41
+ blog: BlogContext<C>;
42
+ ctx: AppContext<C>;
43
+ }>;
44
+ type BlogLayout<C extends ConfigContext = ConfigContext> = ComponentType<{
45
+ lang?: string;
46
+ children: ReactNode;
47
+ blog: BlogContext<C>;
48
+ ctx: AppContext<C>;
49
+ }>;
50
+ type BlogIndexPage<C extends ConfigContext = ConfigContext> = ComponentType<{
51
+ lang?: string;
52
+ blog: BlogContext<C>;
53
+ ctx: AppContext<C>;
54
+ }>;
55
+ type BlogTagsPage<C extends ConfigContext = ConfigContext> = ComponentType<{
56
+ lang?: string;
57
+ blog: BlogContext<C>;
58
+ ctx: AppContext<C>;
59
+ }>;
60
+ type BlogTagPage<C extends ConfigContext = ConfigContext> = ComponentType<{
61
+ lang?: string;
62
+ tag: string;
63
+ blog: BlogContext<C>;
64
+ ctx: AppContext<C>;
65
+ }>;
66
+ declare function blogPlugin<C extends ConfigContext = ConfigContext>({
67
+ paths,
68
+ isBlog,
69
+ layouts
70
+ }?: BlogPluginOptions<C>): ServerPlugin<C>;
71
+ //#endregion
72
+ export { BlogContext, BlogIndexPage, BlogLayout, BlogLayoutPage, BlogPluginOptions, BlogTagPage, BlogTagsPage, blogPlugin };
@@ -0,0 +1,175 @@
1
+ import { joinPathname } from "../lib/join-pathname.js";
2
+ import { createBlogIndexPage } from "../layouts/blog.index.js";
3
+ import { groupTags, groupTagsI18n } from "../lib/shared/blog.js";
4
+ import { createBlogTagPage, createBlogTagsPage } from "../layouts/blog.tags.js";
5
+ import { createBlogLayout, createBlogLayoutPage } from "../layouts/blog.js";
6
+ import { jsx } from "react/jsx-runtime";
7
+ import { unstable_notFound } from "waku/router/server";
8
+ //#region src/plugins/blog.tsx
9
+ function blogPlugin({ paths = {}, isBlog = (page) => page.type === "blog", layouts = {} } = {}) {
10
+ const blogCtx = {
11
+ indexPath: paths.index ?? "/blog",
12
+ tagsPath: paths.tags ?? "/blog/tags",
13
+ isBlog
14
+ };
15
+ return { async createPages({ createPage, createLayout }) {
16
+ const renderMode = this.mode === "dynamic" ? "dynamic" : "static";
17
+ const source = await this.getLoader();
18
+ const blogPages = [];
19
+ for (const page of source.getPages()) {
20
+ if (!isBlog.call(this, page)) continue;
21
+ blogPages.push(page);
22
+ this.markResolved(page);
23
+ }
24
+ const Layout = layouts.layout ?? createBlogLayout();
25
+ const Page = layouts.page ?? createBlogLayoutPage();
26
+ if (this.i18nConfig) {
27
+ createLayout({
28
+ render: renderMode,
29
+ path: "/[lang]/(blog)",
30
+ component: ({ lang, children }) => {
31
+ return /* @__PURE__ */ jsx(Layout, {
32
+ lang,
33
+ blog: blogCtx,
34
+ ctx: this,
35
+ children
36
+ });
37
+ }
38
+ });
39
+ createPage({
40
+ render: renderMode,
41
+ path: "/[lang]/(blog)/[...slugs]",
42
+ staticPaths: blogPages.map((page) => [page.locale, ...page.slugs]),
43
+ component: async ({ slugs, lang }) => {
44
+ const page = (await this.getLoader()).getPage(slugs, lang);
45
+ if (!page || !isBlog.call(this, page)) unstable_notFound();
46
+ return /* @__PURE__ */ jsx(Page, {
47
+ lang,
48
+ slugs,
49
+ blog: blogCtx,
50
+ page,
51
+ ctx: this
52
+ });
53
+ }
54
+ });
55
+ if (blogCtx.indexPath !== false) {
56
+ const IndexPage = layouts.index ?? createBlogIndexPage();
57
+ createPage({
58
+ render: renderMode,
59
+ path: joinPathname("/[lang]/(blog)", blogCtx.indexPath),
60
+ staticPaths: Object.keys(this.i18nConfig.languages),
61
+ component: ({ lang }) => {
62
+ return /* @__PURE__ */ jsx(IndexPage, {
63
+ lang,
64
+ blog: blogCtx,
65
+ ctx: this
66
+ });
67
+ }
68
+ });
69
+ }
70
+ if (blogCtx.tagsPath !== false) {
71
+ const TagsPage = layouts.tags ?? createBlogTagsPage();
72
+ const TagPage = layouts.tag ?? createBlogTagPage();
73
+ createPage({
74
+ path: joinPathname("/[lang]/(blog)", blogCtx.tagsPath),
75
+ render: renderMode,
76
+ staticPaths: Object.keys(this.i18nConfig.languages),
77
+ component: ({ lang }) => {
78
+ return /* @__PURE__ */ jsx(TagsPage, {
79
+ lang,
80
+ blog: blogCtx,
81
+ ctx: this
82
+ });
83
+ }
84
+ });
85
+ const groupedTags = await groupTagsI18n(this, blogPages);
86
+ const staticPaths = [];
87
+ for (const [locale, tags] of groupedTags) for (const tag of tags.keys()) staticPaths.push([locale, tag]);
88
+ createPage({
89
+ path: joinPathname("/[lang]/(blog)", blogCtx.tagsPath, "[tag]"),
90
+ render: renderMode,
91
+ staticPaths,
92
+ component: ({ lang, tag }) => {
93
+ return /* @__PURE__ */ jsx(TagPage, {
94
+ lang,
95
+ tag,
96
+ blog: blogCtx,
97
+ ctx: this
98
+ });
99
+ }
100
+ });
101
+ }
102
+ } else {
103
+ createLayout({
104
+ render: renderMode,
105
+ path: "/(blog)",
106
+ component: ({ children }) => {
107
+ return /* @__PURE__ */ jsx(Layout, {
108
+ blog: blogCtx,
109
+ ctx: this,
110
+ children
111
+ });
112
+ }
113
+ });
114
+ createPage({
115
+ render: renderMode,
116
+ path: "/(blog)/[...slugs]",
117
+ staticPaths: blogPages.map((page) => page.slugs),
118
+ component: async ({ slugs }) => {
119
+ const page = (await this.getLoader()).getPage(slugs);
120
+ if (!page || !isBlog.call(this, page)) unstable_notFound();
121
+ return /* @__PURE__ */ jsx(Page, {
122
+ blog: blogCtx,
123
+ slugs,
124
+ page,
125
+ ctx: this
126
+ });
127
+ }
128
+ });
129
+ if (blogCtx.indexPath !== false) {
130
+ const IndexPage = layouts.index ?? createBlogIndexPage();
131
+ createPage({
132
+ render: renderMode,
133
+ path: joinPathname("/(blog)", blogCtx.indexPath),
134
+ staticPaths: [],
135
+ component: () => {
136
+ return /* @__PURE__ */ jsx(IndexPage, {
137
+ blog: blogCtx,
138
+ ctx: this
139
+ });
140
+ }
141
+ });
142
+ }
143
+ if (blogCtx.tagsPath !== false) {
144
+ const TagsPage = layouts.tags ?? createBlogTagsPage();
145
+ const TagPage = layouts.tag ?? createBlogTagPage();
146
+ createPage({
147
+ path: joinPathname("/(blog)", blogCtx.tagsPath),
148
+ render: renderMode,
149
+ staticPaths: [],
150
+ component: () => {
151
+ return /* @__PURE__ */ jsx(TagsPage, {
152
+ blog: blogCtx,
153
+ ctx: this
154
+ });
155
+ }
156
+ });
157
+ const grouped = await groupTags(this, blogPages);
158
+ createPage({
159
+ path: joinPathname("/(blog)", blogCtx.tagsPath, "[tag]"),
160
+ render: renderMode,
161
+ staticPaths: Array.from(grouped.keys()),
162
+ component: ({ tag }) => {
163
+ return /* @__PURE__ */ jsx(TagPage, {
164
+ tag,
165
+ blog: blogCtx,
166
+ ctx: this
167
+ });
168
+ }
169
+ });
170
+ }
171
+ }
172
+ } };
173
+ }
174
+ //#endregion
175
+ export { blogPlugin };
@@ -4,7 +4,7 @@ import { Awaitable, ServerPlugin } from "../lib/types.js";
4
4
 
5
5
  //#region src/plugins/llms.txt.d.ts
6
6
  interface LLMsOptions<C extends ConfigContext = ConfigContext> {
7
- getLLMText?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<string>;
7
+ getLLMText?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<string | undefined>;
8
8
  }
9
9
  declare function llmsPlugin<C extends ConfigContext = ConfigContext>(options?: LLMsOptions<NoInfer<C>>): ServerPlugin<C>;
10
10
  //#endregion
@@ -7,7 +7,6 @@ function llmsPlugin(options = {}) {
7
7
  const txt = await adapter["core:get-text"]?.call(this, page);
8
8
  if (txt !== void 0) return `# ${page.data.title} (${page.url})\n\n${txt}`;
9
9
  }
10
- throw new Error("[Fumapress] Please specify the `getLLMText()` option in llmsPlugin()");
11
10
  } } = options;
12
11
  return {
13
12
  init() {
@@ -33,9 +32,9 @@ function llmsPlugin(options = {}) {
33
32
  render: defaultRenderMode,
34
33
  path: "/llms-full.txt",
35
34
  handler: async () => {
36
- const scan = (await this.getLoader()).getPages().map(getLLMText);
37
- const scanned = await Promise.all(scan);
38
- return new Response(scanned.join("\n\n"));
35
+ const source = await this.getLoader();
36
+ const scanned = await Promise.all(source.getPages().map(getLLMText));
37
+ return new Response(scanned.filter((item) => item !== void 0).join("\n\n"));
39
38
  }
40
39
  });
41
40
  createApiIsomorphic({
@@ -45,7 +44,8 @@ function llmsPlugin(options = {}) {
45
44
  handler: async (_req, { params }) => {
46
45
  const page = (await this.getLoader()).getPage(markdownPathToSlugs(params.slugs), params.lang);
47
46
  if (!page) unstable_notFound();
48
- return new Response(await getLLMText(page), { headers: { "Content-Type": "text/markdown" } });
47
+ const txt = await getLLMText(page);
48
+ return new Response(txt ?? "", { headers: { "Content-Type": "text/markdown" } });
49
49
  }
50
50
  });
51
51
  }
@@ -0,0 +1,13 @@
1
+ import { ConfigContext } from "../config.js";
2
+ import { ServerPlugin } from "../lib/types.js";
3
+ import { OpenAPIAdapterOptions } from "../adapters/openapi.js";
4
+ import { openapiPlugin as openapiLoaderPlugin } from "fumadocs-openapi/server";
5
+
6
+ //#region src/plugins/openapi.d.ts
7
+ type OpenAPIOptions = OpenAPIAdapterOptions;
8
+ /**
9
+ * this will register the OpenAPI adapter & required layout configs.
10
+ */
11
+ declare function openapiPlugin<C extends ConfigContext>(options: OpenAPIOptions): ServerPlugin<C>;
12
+ //#endregion
13
+ export { OpenAPIOptions, openapiLoaderPlugin, openapiPlugin };
@@ -0,0 +1,21 @@
1
+ import { fumadocsOpenAPI, isOpenAPI } from "../adapters/openapi.js";
2
+ import { openapiPlugin as openapiLoaderPlugin } from "fumadocs-openapi/server";
3
+ //#region src/plugins/openapi.tsx
4
+ /**
5
+ * this will register the OpenAPI adapter & required layout configs.
6
+ */
7
+ function openapiPlugin(options) {
8
+ function initRenderers(data) {
9
+ (data.renderers ??= []).push(function(data) {
10
+ if (isOpenAPI(this.page.data)) data.pageProps.full ??= true;
11
+ return data;
12
+ });
13
+ }
14
+ return { init() {
15
+ this.adapters.push(fumadocsOpenAPI(options));
16
+ initRenderers(this.data["core:docs-layout"] ??= {});
17
+ initRenderers(this.data["core:notebook-layout"] ??= {});
18
+ } };
19
+ }
20
+ //#endregion
21
+ export { openapiLoaderPlugin, openapiPlugin };