fumapress 0.5.0 → 0.5.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/css/generated.css +217 -3
- package/dist/adapters/mdx.d.ts +1 -1
- package/dist/client.d.ts +2 -23
- package/dist/client.js +3 -9
- package/dist/components/blog.js +4 -4
- package/dist/components/image.d.ts +30 -0
- package/dist/components/image.js +143 -0
- package/dist/components/link.d.ts +25 -0
- package/dist/components/link.js +18 -0
- package/dist/components/provider.d.ts +6 -0
- package/dist/components/provider.js +39 -0
- package/dist/config.d.ts +73 -30
- package/dist/config.js +14 -7
- package/dist/image.d.ts +2 -0
- package/dist/image.js +2 -0
- package/dist/index.d.ts +2 -2
- package/dist/layouts/blog.d.ts +1 -1
- package/dist/layouts/blog.index.js +3 -3
- package/dist/layouts/blog.js +3 -3
- package/dist/layouts/blog.tags.js +2 -2
- package/dist/layouts/docs.d.ts +2 -2
- package/dist/layouts/home.d.ts +1 -1
- package/dist/layouts/notebook.d.ts +2 -2
- package/dist/layouts/root.d.ts +5 -3
- package/dist/layouts/root.js +13 -16
- package/dist/layouts/switch.d.ts +2 -2
- package/dist/lib/pathname.js +14 -0
- package/dist/lib/shared.d.ts +10 -15
- package/dist/lib/shared.js +68 -33
- package/dist/lib/types.d.ts +30 -16
- package/dist/node_modules/.pnpm/http-cache-semantics@4.2.0/node_modules/http-cache-semantics/index.js +596 -0
- package/dist/plugins/blog.d.ts +3 -6
- package/dist/plugins/blog.js +6 -6
- package/dist/plugins/flexsearch.d.ts +1 -1
- package/dist/plugins/image/self-hosted.client.js +40 -0
- package/dist/plugins/image/self-hosted.d.ts +29 -0
- package/dist/plugins/image/self-hosted.js +30 -0
- package/dist/plugins/image/self-hosted.utils.js +270 -0
- package/dist/plugins/image/vercel.client.js +34 -0
- package/dist/plugins/image/vercel.d.ts +39 -0
- package/dist/plugins/image/vercel.enhancer.d.ts +10 -0
- package/dist/plugins/image/vercel.enhancer.js +16 -0
- package/dist/plugins/image/vercel.js +30 -0
- package/dist/plugins/image/vercel.utils.d.ts +10 -0
- package/dist/plugins/image/vercel.utils.js +30 -0
- package/dist/plugins/link-validation.d.ts +24 -0
- package/dist/plugins/link-validation.js +30 -0
- package/dist/plugins/llms.txt.d.ts +1 -1
- package/dist/plugins/llms.txt.js +2 -2
- package/dist/plugins/openapi.d.ts +2 -0
- package/dist/plugins/openapi.js +8 -1
- package/dist/plugins/orama-search.d.ts +1 -1
- package/dist/plugins/sitemap.d.ts +196 -0
- package/dist/plugins/sitemap.js +90 -0
- package/dist/plugins/takumi.d.ts +1 -1
- package/dist/plugins/takumi.js +3 -4
- package/dist/router/fs.js +1 -1
- package/dist/router/index.d.ts +17 -0
- package/dist/{router.js → router/index.js} +41 -26
- package/dist/vite.js +10 -10
- package/package.json +27 -9
- package/dist/lib/join-pathname.js +0 -9
- package/dist/router.d.ts +0 -15
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/lib/pathname.ts
|
|
2
|
+
function joinPathname(...paths) {
|
|
3
|
+
let combined = paths.join("/").replaceAll(/\/+/g, "/");
|
|
4
|
+
if (!combined.startsWith("/")) combined = "/" + combined;
|
|
5
|
+
if (combined.endsWith("/")) combined = combined.slice(0, -1);
|
|
6
|
+
return combined;
|
|
7
|
+
}
|
|
8
|
+
const PATHNAME_SEGMENT_REGEX = /^[A-Za-z0-9\-._~!$&'()*+,;=:@]+$/;
|
|
9
|
+
/** Check if the string is a full pathname (one that does not include `.` or `..`) */
|
|
10
|
+
function isPlainPathname(s) {
|
|
11
|
+
return s.startsWith("/") && s.slice(1).split("/").every((seg) => seg !== "." && seg !== ".." && PATHNAME_SEGMENT_REGEX.test(seg));
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { isPlainPathname, joinPathname };
|
package/dist/lib/shared.d.ts
CHANGED
|
@@ -1,30 +1,28 @@
|
|
|
1
|
-
import { BuildMode, ConfigContext, Layouts
|
|
1
|
+
import { BaseConfig, BuildMode, ConfigContext, Layouts } from "../config.js";
|
|
2
2
|
import { Adapter, AppContextData, Awaitable, ServerPlugin } from "./types.js";
|
|
3
|
-
import { LoaderOutput } from "fumadocs-core/source";
|
|
4
3
|
import { ReactNode } from "react";
|
|
4
|
+
import { LoaderOutput } from "fumadocs-core/source";
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
|
-
import { I18nConfig, SingularTranslationsAPI, TranslationsAPI } from "fumadocs-core/i18n";
|
|
7
|
-
import { Translations } from "fumadocs-ui/i18n";
|
|
8
6
|
//#region src/lib/shared.d.ts
|
|
9
7
|
interface AppContext<C extends ConfigContext = ConfigContext> {
|
|
10
8
|
mode: BuildMode;
|
|
11
|
-
getLoader: () => Awaitable<LoaderOutput<C
|
|
9
|
+
getLoader: () => Awaitable<LoaderOutput<C>>;
|
|
12
10
|
plugins: ServerPlugin<C>[];
|
|
13
11
|
adapters: Adapter<C>[];
|
|
14
12
|
layouts: Layouts<C>;
|
|
13
|
+
/** revalidate [dynamic content sources](https://fumadocs.dev/docs/headless/source-api/source#dynamic-source) */
|
|
14
|
+
revalidateLoader: (() => Promise<void>) | (C["source"] extends string ? (name: C["source"]) => Promise<void> : never);
|
|
15
|
+
/** invalidate [dynamic content sources](https://fumadocs.dev/docs/headless/source-api/source#dynamic-source) */
|
|
16
|
+
invalidateLoader: (() => void) | (C["source"] extends string ? (name: C["source"]) => void : never);
|
|
15
17
|
/** always `undefined`, easier way to infer types */
|
|
16
18
|
$context: C;
|
|
17
19
|
/**
|
|
18
20
|
* custom data in app context, can be referenced from plugins/pages etc
|
|
19
21
|
*/
|
|
20
22
|
data: AppContextData & Record<string, unknown>;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}> | SingularTranslationsAPI<{
|
|
25
|
-
ui: Translations;
|
|
26
|
-
}>;
|
|
27
|
-
metaConfig?: MetaConfig<C>;
|
|
23
|
+
translationsConfig?: BaseConfig<C>["translations"];
|
|
24
|
+
i18nConfig?: C["i18n"];
|
|
25
|
+
metaConfig?: BaseConfig<C>["meta"];
|
|
28
26
|
siteConfig: {
|
|
29
27
|
name: string;
|
|
30
28
|
baseUrl?: string;
|
|
@@ -36,9 +34,6 @@ interface AppContext<C extends ConfigContext = ConfigContext> {
|
|
|
36
34
|
};
|
|
37
35
|
};
|
|
38
36
|
}
|
|
39
|
-
declare global {
|
|
40
|
-
var appContextTemp: AppContext | undefined;
|
|
41
|
-
}
|
|
42
37
|
declare function getPressContext<C extends ConfigContext = ConfigContext>(): AppContext<C>;
|
|
43
38
|
type TransformChildren<T> = Omit<T, "children"> & {
|
|
44
39
|
children?: ((nodes: ReactNode) => ReactNode)[];
|
package/dist/lib/shared.js
CHANGED
|
@@ -3,63 +3,98 @@ import { getGitRootDir } from "./fs.js";
|
|
|
3
3
|
import { require_deepmerge } from "../node_modules/.pnpm/@fastify_deepmerge@3.2.1/node_modules/@fastify/deepmerge/index.js";
|
|
4
4
|
import { disableSearchPlugin } from "../plugins/internal/disable-search.js";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { Fragment as Fragment$1, isValidElement } from "react";
|
|
6
7
|
import path from "node:path";
|
|
7
8
|
import { loader } from "fumadocs-core/source";
|
|
8
|
-
import { Fragment as Fragment$1, isValidElement } from "react";
|
|
9
9
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
10
|
+
import { dynamicLoader } from "fumadocs-core/source/dynamic";
|
|
10
11
|
//#region src/lib/shared.tsx
|
|
11
12
|
var import_deepmerge = /* @__PURE__ */ __toESM(require_deepmerge());
|
|
12
13
|
const appContext = new AsyncLocalStorage({ name: "fumapress:core" });
|
|
13
14
|
function getPressContext() {
|
|
14
|
-
|
|
15
|
-
if (!store) store = global.appContextTemp;
|
|
16
|
-
else delete global.appContextTemp;
|
|
15
|
+
const store = appContext.getStore();
|
|
17
16
|
if (!store) throw new Error("[Fumapress] Missing server context for Fumapress, make sure to use the middlewares from createRouter()");
|
|
18
17
|
return store;
|
|
19
18
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
const PLUGIN_ORDER = {
|
|
20
|
+
pre: -1,
|
|
21
|
+
post: 1,
|
|
22
|
+
_: 0
|
|
23
|
+
};
|
|
24
|
+
function flattenPlugins(plugins) {
|
|
25
|
+
const out = [];
|
|
26
|
+
for (const plugin of plugins) {
|
|
27
|
+
if (!plugin) continue;
|
|
28
|
+
if (Array.isArray(plugin)) out.push(...flattenPlugins(plugin));
|
|
29
|
+
else out.push(plugin);
|
|
31
30
|
}
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
function resolvePlugins(plugins) {
|
|
34
|
+
return flattenPlugins(plugins).sort((a, b) => PLUGIN_ORDER[a.enforce ?? "_"] - PLUGIN_ORDER[b.enforce ?? "_"]);
|
|
35
|
+
}
|
|
36
|
+
async function initApp(builder) {
|
|
37
|
+
const config = builder.get();
|
|
38
|
+
const { translations, site, mode = "default", layouts } = config;
|
|
39
|
+
const plugins = resolvePlugins([...config.plugins, disableSearchPlugin()]);
|
|
40
|
+
const ctx = {
|
|
41
|
+
$context: void 0,
|
|
34
42
|
getLoader() {
|
|
35
|
-
|
|
36
|
-
if (config.loader) return config.loader;
|
|
37
|
-
console.warn("[Fumapress] loader is not specified in your config, is it a mistake?");
|
|
38
|
-
return EMPTY ??= loader({}, { baseUrl: "/" });
|
|
43
|
+
throw new Error("[Fumapress] Content loader is not initialized yet, please access it after init()");
|
|
39
44
|
},
|
|
45
|
+
revalidateLoader: () => Promise.resolve(void 0),
|
|
46
|
+
invalidateLoader: () => void 0,
|
|
47
|
+
i18nConfig: translations && "config" in translations ? translations.config : void 0,
|
|
40
48
|
layouts: {
|
|
41
49
|
...layouts,
|
|
42
50
|
root: layouts.root ?? (await import("../layouts/root.js")).createRootLayout(),
|
|
43
51
|
page: layouts.page ?? (await import("../layouts/docs.js")).createDocsLayoutPage(),
|
|
44
52
|
notFound: layouts.notFound ?? (await import("fumadocs-ui/layouts/home/not-found")).DefaultNotFound
|
|
45
53
|
},
|
|
46
|
-
plugins
|
|
47
|
-
adapters: config.
|
|
48
|
-
$context: void 0,
|
|
54
|
+
plugins,
|
|
55
|
+
adapters: config.adapters,
|
|
49
56
|
data: {},
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
mode: config.mode ?? "default",
|
|
57
|
+
translationsConfig: translations,
|
|
58
|
+
mode,
|
|
53
59
|
metaConfig: config.meta,
|
|
54
60
|
siteConfig: {
|
|
55
|
-
name:
|
|
56
|
-
baseUrl:
|
|
57
|
-
git:
|
|
58
|
-
...
|
|
59
|
-
rootDir:
|
|
61
|
+
name: site?.name ?? "Fumapress",
|
|
62
|
+
baseUrl: site?.baseUrl ?? getDefaultBaseUrl(),
|
|
63
|
+
git: site?.git ? {
|
|
64
|
+
...site.git,
|
|
65
|
+
rootDir: site.git.rootDir ?? getGitRootDir() ?? process.cwd()
|
|
60
66
|
} : void 0
|
|
61
67
|
}
|
|
62
68
|
};
|
|
69
|
+
if ("loader" in config && config.loader) {
|
|
70
|
+
ctx.i18nConfig ??= config.loader._i18n;
|
|
71
|
+
ctx.getLoader = () => config.loader;
|
|
72
|
+
} else if ("content" in config) ctx.i18nConfig ??= config.i18n;
|
|
73
|
+
else {
|
|
74
|
+
console.warn("[Fumapress] loader is not specified in your config, is it a mistake?");
|
|
75
|
+
const emptyLoader = loader({}, { baseUrl: "/" });
|
|
76
|
+
ctx.getLoader = () => emptyLoader;
|
|
77
|
+
}
|
|
78
|
+
for (const plugin of plugins) await plugin.init?.call(ctx);
|
|
79
|
+
if ("content" in config) {
|
|
80
|
+
let loaderOptions = {
|
|
81
|
+
baseUrl: "/",
|
|
82
|
+
i18n: ctx.i18nConfig,
|
|
83
|
+
...config.loaderOptions
|
|
84
|
+
};
|
|
85
|
+
for (const plugin of plugins) {
|
|
86
|
+
if (!plugin.configureLoader) continue;
|
|
87
|
+
loaderOptions = await plugin.configureLoader.call(ctx, loaderOptions);
|
|
88
|
+
}
|
|
89
|
+
const source = dynamicLoader(config.content, loaderOptions);
|
|
90
|
+
ctx.revalidateLoader = source.revalidate.bind(source);
|
|
91
|
+
ctx.invalidateLoader = source.invalidate.bind(source);
|
|
92
|
+
ctx.getLoader = () => {
|
|
93
|
+
if (config.loaderOptions?.alwaysRevalidate) source.invalidate();
|
|
94
|
+
return source.get();
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return ctx;
|
|
63
98
|
}
|
|
64
99
|
function getDefaultBaseUrl() {
|
|
65
100
|
console.warn("[Fumapress] It is recommended to specify \"site.baseUrl\" in your config for better SEO.");
|
|
@@ -140,4 +175,4 @@ const mergeLayoutConfigs = (0, import_deepmerge.default)({
|
|
|
140
175
|
}
|
|
141
176
|
});
|
|
142
177
|
//#endregion
|
|
143
|
-
export { appContext, baseLayoutProps, createTransformChildren, getCreationDate, getGitHubFileUrl, getLastModifiedDate, getPressContext,
|
|
178
|
+
export { appContext, baseLayoutProps, createTransformChildren, getCreationDate, getGitHubFileUrl, getLastModifiedDate, getPressContext, initApp, mergeLayoutConfigs, renderBody, renderPageMeta, renderRootMeta, renderToc };
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -3,25 +3,31 @@ import { ConfigContext } from "../config.js";
|
|
|
3
3
|
import { DocsLayoutContextData } from "../layouts/docs.js";
|
|
4
4
|
import { HomeLayoutContextData } from "../layouts/home.js";
|
|
5
5
|
import { NotebookLayoutContextData } from "../layouts/notebook.js";
|
|
6
|
-
import {
|
|
6
|
+
import { RootLayoutContextData } from "../layouts/root.js";
|
|
7
7
|
import { ReactNode } from "react";
|
|
8
|
-
import {
|
|
8
|
+
import { ContentStorage, LoaderOptions, LoaderPluginOption, Page } from "fumadocs-core/source";
|
|
9
|
+
import { I18nConfig } from "fumadocs-core/i18n";
|
|
10
|
+
import { CreateApi, CreateLayout, CreatePage, CreateRoot, CreateSlice, createPages } from "waku/router/server";
|
|
9
11
|
import { TOCItemType } from "fumadocs-core/toc";
|
|
10
12
|
import { StructuredData } from "fumadocs-core/mdx-plugins";
|
|
11
|
-
import { RootProviderProps } from "fumadocs-ui/provider/base";
|
|
12
13
|
import { MiddlewareHandler } from "hono";
|
|
14
|
+
import { unstable_createServerEntryAdapter } from "waku/adapter-builders";
|
|
13
15
|
|
|
14
16
|
//#region src/lib/types.d.ts
|
|
15
17
|
type Awaitable<T> = T | Promise<T>;
|
|
16
18
|
/** allow content sources to implement interfaces for pages, instead of requiring consumers to specify manually */
|
|
17
19
|
interface Adapter<C extends ConfigContext = ConfigContext> {
|
|
18
|
-
"core:get-text"?: (this: AppContext<C>, page: C["
|
|
19
|
-
"core:get-structured-data"?: (this: AppContext<C>, page: C["
|
|
20
|
-
"core:render-body"?: (this: AppContext<C>, page: C["
|
|
21
|
-
"core:render-toc"?: (this: AppContext<C>, page: C["
|
|
22
|
-
"core:get-creation-date"?: (this: AppContext<C>, page: C["
|
|
23
|
-
"core:get-modified-date"?: (this: AppContext<C>, page: C["
|
|
24
|
-
"blog:get-tags"?: (this: AppContext<C>, page: C["
|
|
20
|
+
"core:get-text"?: (this: AppContext<C>, page: C["page"]) => Awaitable<string | undefined>;
|
|
21
|
+
"core:get-structured-data"?: (this: AppContext<C>, page: C["page"]) => Awaitable<StructuredData | undefined>;
|
|
22
|
+
"core:render-body"?: (this: AppContext<C>, page: C["page"]) => Awaitable<ReactNode>;
|
|
23
|
+
"core:render-toc"?: (this: AppContext<C>, page: C["page"]) => Awaitable<TOCItemType[] | undefined>;
|
|
24
|
+
"core:get-creation-date"?: (this: AppContext<C>, page: C["page"]) => Awaitable<Date | undefined>;
|
|
25
|
+
"core:get-modified-date"?: (this: AppContext<C>, page: C["page"]) => Awaitable<Date | undefined>;
|
|
26
|
+
"blog:get-tags"?: (this: AppContext<C>, page: C["page"]) => Awaitable<string[] | undefined>;
|
|
27
|
+
}
|
|
28
|
+
/** make plugins an array for easier modification */
|
|
29
|
+
interface PressLoaderOptions<S extends ContentStorage = ContentStorage, I18n extends I18nConfig | undefined = I18nConfig | undefined> extends Omit<LoaderOptions<S, I18n>, "plugins"> {
|
|
30
|
+
plugins?: LoaderPluginOption[];
|
|
25
31
|
}
|
|
26
32
|
interface ServerPlugin<C extends ConfigContext = ConfigContext> {
|
|
27
33
|
name?: string;
|
|
@@ -37,17 +43,22 @@ interface ServerPlugin<C extends ConfigContext = ConfigContext> {
|
|
|
37
43
|
* - `false`: render not found (will also exclude from static pre-rendering).
|
|
38
44
|
* - `undefined`: fallback to default.
|
|
39
45
|
*/
|
|
40
|
-
resolvePage?: (this: AppContext<C>, page: C["
|
|
46
|
+
resolvePage?: (this: AppContext<C>, page: C["page"]) => Awaitable<C["page"] | false | undefined>;
|
|
41
47
|
/**
|
|
42
48
|
* Override the page renderer, use default fallback if `undefined` is returned.
|
|
43
49
|
*/
|
|
44
50
|
renderPage?: (this: AppContext<C>, env: {
|
|
45
|
-
page: C["
|
|
51
|
+
page: C["page"];
|
|
46
52
|
fallback: ReactNode;
|
|
47
53
|
lang?: string;
|
|
48
54
|
slugs: string[];
|
|
49
55
|
}) => Awaitable<ReactNode>;
|
|
50
|
-
|
|
56
|
+
/** resolve content loader options */
|
|
57
|
+
configureLoader?: (this: AppContext<C>, options: PressLoaderOptions) => Awaitable<PressLoaderOptions>;
|
|
58
|
+
/** create Hono middlewares */
|
|
59
|
+
createMiddlewares?: (this: AppContext<C>) => Awaitable<MiddlewareHandler[] | undefined>;
|
|
60
|
+
unstable_onSSGRequest?: <T>(req: Request, next: () => T) => T;
|
|
61
|
+
unstable_onServerEntry?: (entry: ReturnType<ReturnType<typeof unstable_createServerEntryAdapter>>) => ReturnType<ReturnType<typeof unstable_createServerEntryAdapter>>;
|
|
51
62
|
}
|
|
52
63
|
interface BaseRouteFns {
|
|
53
64
|
createPage: CreatePage;
|
|
@@ -56,6 +67,7 @@ interface BaseRouteFns {
|
|
|
56
67
|
createApi: CreateApi;
|
|
57
68
|
createSlice: CreateSlice;
|
|
58
69
|
}
|
|
70
|
+
type CreatePagesResult = ReturnType<typeof createPages>;
|
|
59
71
|
interface RouteFns extends BaseRouteFns {
|
|
60
72
|
createApiIsomorphic: (config: {
|
|
61
73
|
render: "static" | "dynamic";
|
|
@@ -65,15 +77,17 @@ interface RouteFns extends BaseRouteFns {
|
|
|
65
77
|
params: Record<string, string | string[]>;
|
|
66
78
|
}) => Promise<Response>;
|
|
67
79
|
}) => void;
|
|
80
|
+
/** access `createPages()` output */
|
|
81
|
+
unstable_getCreated: () => CreatePagesResult;
|
|
68
82
|
}
|
|
69
|
-
type ServerPluginOption<C extends ConfigContext = ConfigContext> = ServerPlugin<C> | ServerPluginOption<C>[];
|
|
83
|
+
type ServerPluginOption<C extends ConfigContext = ConfigContext> = ServerPlugin<C> | false | undefined | null | ServerPluginOption<C>[];
|
|
70
84
|
/** can be extended from other libraries */
|
|
71
85
|
interface AppContextData {
|
|
72
86
|
"core:page-meta"?: ((page: Page) => ReactNode)[];
|
|
73
87
|
"core:notebook-layout"?: NotebookLayoutContextData;
|
|
74
88
|
"core:docs-layout"?: DocsLayoutContextData;
|
|
75
89
|
"core:home-layout"?: HomeLayoutContextData;
|
|
76
|
-
"core:provider"?:
|
|
90
|
+
"core:provider"?: RootLayoutContextData;
|
|
77
91
|
}
|
|
78
92
|
/**
|
|
79
93
|
* For file-system router, route files can export a `getConfig()` function that returns a `RouteConfig` object.
|
|
@@ -88,4 +102,4 @@ interface RouteConfig {
|
|
|
88
102
|
autoI18n?: boolean;
|
|
89
103
|
}
|
|
90
104
|
//#endregion
|
|
91
|
-
export { Adapter, AppContextData, Awaitable, RouteConfig, RouteFns, ServerPlugin, ServerPluginOption };
|
|
105
|
+
export { Adapter, AppContextData, Awaitable, PressLoaderOptions, RouteConfig, RouteFns, ServerPlugin, ServerPluginOption };
|