fumapress 0.2.3 → 0.2.5
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 +108 -0
- package/dist/adapters/mdx.d.ts +12 -0
- package/dist/adapters/mdx.js +37 -0
- package/dist/components/flexsearch-static.js +24 -0
- package/dist/components/orama-search-static.js +34 -0
- package/dist/config.d.ts +84 -0
- package/dist/config.js +36 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/layouts/docs.d.ts +37 -0
- package/dist/layouts/docs.js +83 -0
- package/dist/layouts/home.d.ts +28 -0
- package/dist/layouts/home.js +35 -0
- package/dist/layouts/notebook.d.ts +37 -0
- package/dist/layouts/notebook.js +83 -0
- package/dist/layouts/root.d.ts +10 -0
- package/dist/layouts/root.js +38 -0
- package/dist/lib/fs.js +18 -0
- package/dist/lib/shared.d.ts +46 -0
- package/dist/lib/shared.js +70 -0
- package/dist/lib/types.d.ts +33 -0
- package/dist/lib/vitefu.js +179 -0
- package/dist/plugins/flexsearch.d.ts +14 -0
- package/dist/plugins/flexsearch.js +35 -0
- package/dist/plugins/llms.txt.d.ts +11 -0
- package/dist/plugins/llms.txt.js +72 -0
- package/dist/plugins/orama-search.d.ts +14 -0
- package/dist/plugins/orama-search.js +35 -0
- package/dist/plugins/takumi.d.ts +16 -0
- package/dist/plugins/takumi.js +79 -0
- package/dist/router.d.ts +11 -0
- package/dist/router.js +133 -0
- package/dist/vite.d.ts +6 -0
- package/dist/vite.js +50 -0
- package/package.json +2 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { renderRootMeta } from "../lib/shared.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import styles from "virtual:root.css?inline";
|
|
4
|
+
import { RootProvider } from "fumadocs-ui/provider/waku";
|
|
5
|
+
//#region src/layouts/root.tsx
|
|
6
|
+
function createRootLayout(options) {
|
|
7
|
+
return async function(props) {
|
|
8
|
+
const { children, lang, i18nConfig, data } = props;
|
|
9
|
+
const hooks = data["core:provider"];
|
|
10
|
+
let providerProps = { ...options?.providerProps };
|
|
11
|
+
if (i18nConfig) {
|
|
12
|
+
const { languages } = i18nConfig;
|
|
13
|
+
providerProps.i18n ??= {
|
|
14
|
+
locale: lang,
|
|
15
|
+
locales: Object.entries(languages).map(([k, v]) => ({
|
|
16
|
+
name: v.displayName,
|
|
17
|
+
locale: k
|
|
18
|
+
})),
|
|
19
|
+
translations: lang ? languages[lang]?.translations : void 0
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
if (hooks) for (const hook of hooks) providerProps = await hook(providerProps);
|
|
23
|
+
return /* @__PURE__ */ jsxs("html", {
|
|
24
|
+
lang: lang ?? "en",
|
|
25
|
+
suppressHydrationWarning: true,
|
|
26
|
+
children: [/* @__PURE__ */ jsxs("head", { children: [/* @__PURE__ */ jsx("style", { children: styles }), renderRootMeta(props)] }), /* @__PURE__ */ jsx("body", {
|
|
27
|
+
"data-version": "1.0",
|
|
28
|
+
className: "flex flex-col min-h-screen",
|
|
29
|
+
children: /* @__PURE__ */ jsx(RootProvider, {
|
|
30
|
+
...providerProps,
|
|
31
|
+
children
|
|
32
|
+
})
|
|
33
|
+
})]
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { createRootLayout };
|
package/dist/lib/fs.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
//#region src/lib/fs.ts
|
|
4
|
+
/**
|
|
5
|
+
* Returns the absolute path to the root directory of the current git repository.
|
|
6
|
+
*/
|
|
7
|
+
function getGitRootDir(startDir = process.cwd()) {
|
|
8
|
+
let dir = startDir;
|
|
9
|
+
while (true) {
|
|
10
|
+
if (existsSync(join(dir, ".git"))) return dir;
|
|
11
|
+
const parent = dirname(dir);
|
|
12
|
+
if (parent === dir) break;
|
|
13
|
+
dir = parent;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { getGitRootDir };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { DocsLayoutContextData } from "../layouts/docs.js";
|
|
2
|
+
import { HomeLayoutContextData } from "../layouts/home.js";
|
|
3
|
+
import { NotebookLayoutContextData } from "../layouts/notebook.js";
|
|
4
|
+
import { BuildMode, Config, ConfigContext, I18nConfig } from "../config.js";
|
|
5
|
+
import { Adapter, Awaitable, ServerPlugin } from "./types.js";
|
|
6
|
+
import { ComponentType, ReactNode } from "react";
|
|
7
|
+
import { RootProviderProps } from "fumadocs-ui/provider/waku";
|
|
8
|
+
import { LoaderOutput, Page } from "fumadocs-core/source";
|
|
9
|
+
|
|
10
|
+
//#region src/lib/shared.d.ts
|
|
11
|
+
interface AppContext<C extends ConfigContext = ConfigContext> {
|
|
12
|
+
mode: BuildMode;
|
|
13
|
+
getLoader: () => Awaitable<LoaderOutput<C["loaderConfig"]>>;
|
|
14
|
+
plugins: ServerPlugin<C>[];
|
|
15
|
+
adapters: Adapter<C>[];
|
|
16
|
+
/** always `undefined`, easier way to infer types */
|
|
17
|
+
$context: C;
|
|
18
|
+
/**
|
|
19
|
+
* custom data in app context, can be referenced from plugins/pages etc
|
|
20
|
+
*/
|
|
21
|
+
data: AppContextData & Record<string, unknown>;
|
|
22
|
+
i18nConfig?: I18nConfig<C["lang"]>;
|
|
23
|
+
metaConfig?: Config<C>["meta"];
|
|
24
|
+
siteConfig: {
|
|
25
|
+
name: string;
|
|
26
|
+
baseUrl?: string;
|
|
27
|
+
git?: {
|
|
28
|
+
user: string;
|
|
29
|
+
repo: string;
|
|
30
|
+
branch: string;
|
|
31
|
+
rootDir: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
interface AppContextData {
|
|
36
|
+
"core:page-meta"?: ((page: Page) => ReactNode)[];
|
|
37
|
+
"core:notebook-layout"?: NotebookLayoutContextData;
|
|
38
|
+
"core:docs-layout"?: DocsLayoutContextData;
|
|
39
|
+
"core:home-layout"?: HomeLayoutContextData;
|
|
40
|
+
"core:provider"?: ((props: RootProviderProps) => Awaitable<RootProviderProps>)[];
|
|
41
|
+
}
|
|
42
|
+
type TransformChildren<T> = Omit<T, "children"> & {
|
|
43
|
+
children?: ((nodes: ReactNode) => ReactNode)[];
|
|
44
|
+
};
|
|
45
|
+
//#endregion
|
|
46
|
+
export { AppContext, AppContextData, TransformChildren };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { getGitRootDir } from "./fs.js";
|
|
2
|
+
import { fumadocsMdx } from "../adapters/mdx.js";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { Fragment } from "react";
|
|
5
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/lib/shared.tsx
|
|
7
|
+
function parseConfig(config) {
|
|
8
|
+
return {
|
|
9
|
+
getLoader() {
|
|
10
|
+
if (typeof config.loader === "function") return config.loader();
|
|
11
|
+
return config.loader;
|
|
12
|
+
},
|
|
13
|
+
plugins: config.plugins ?? [],
|
|
14
|
+
adapters: config.adapters ?? [fumadocsMdx()],
|
|
15
|
+
$context: void 0,
|
|
16
|
+
data: {},
|
|
17
|
+
i18nConfig: config.i18n,
|
|
18
|
+
mode: config.mode ?? "default",
|
|
19
|
+
metaConfig: config.meta,
|
|
20
|
+
siteConfig: {
|
|
21
|
+
name: config.site?.name ?? "Fumapress",
|
|
22
|
+
baseUrl: config.site?.baseUrl,
|
|
23
|
+
git: config.site?.git ? {
|
|
24
|
+
...config.site.git,
|
|
25
|
+
rootDir: config.site.git.rootDir ?? getGitRootDir() ?? process.cwd()
|
|
26
|
+
} : void 0
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function renderRootMeta(context) {
|
|
31
|
+
return context.metaConfig?.root?.call(context);
|
|
32
|
+
}
|
|
33
|
+
function renderPageMeta(page, context) {
|
|
34
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
35
|
+
/* @__PURE__ */ jsx("title", { children: page.data.title }),
|
|
36
|
+
/* @__PURE__ */ jsx("meta", {
|
|
37
|
+
property: "og:title",
|
|
38
|
+
content: page.data.title
|
|
39
|
+
}),
|
|
40
|
+
page.data.description && /* @__PURE__ */ jsx("meta", {
|
|
41
|
+
property: "og:description",
|
|
42
|
+
content: page.data.description
|
|
43
|
+
}),
|
|
44
|
+
context.metaConfig?.page?.call(context, page),
|
|
45
|
+
context.data["core:page-meta"]?.map((hook, i) => /* @__PURE__ */ jsx(Fragment, { children: hook(page) }, i))
|
|
46
|
+
] });
|
|
47
|
+
}
|
|
48
|
+
function getGitHubFileUrl(ctx, absolutePath) {
|
|
49
|
+
const { git } = ctx.siteConfig;
|
|
50
|
+
if (!git) return;
|
|
51
|
+
const p = path.relative(git.rootDir, absolutePath).replaceAll(path.sep, "/");
|
|
52
|
+
if (p.startsWith("../")) return;
|
|
53
|
+
return `https://github.com/${git.user}/${git.repo}/blob/${git.branch}/${p}`;
|
|
54
|
+
}
|
|
55
|
+
function baseOptions(ctx) {
|
|
56
|
+
const { name, git } = ctx.siteConfig;
|
|
57
|
+
return {
|
|
58
|
+
nav: { title: name },
|
|
59
|
+
githubUrl: git ? `https://github.com/${git.user}/${git.repo}` : void 0
|
|
60
|
+
};
|
|
61
|
+
}
|
|
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
|
+
});
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
export { TransformChildrenSlot, baseOptions, getGitHubFileUrl, parseConfig, renderPageMeta, renderRootMeta };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { AppContext } from "./shared.js";
|
|
2
|
+
import { ConfigContext } from "../config.js";
|
|
3
|
+
import { createPages } from "waku";
|
|
4
|
+
import { ReactNode } from "react";
|
|
5
|
+
import { StructuredData } from "fumadocs-core/mdx-plugins";
|
|
6
|
+
import { TOCItemType } from "fumadocs-core/toc";
|
|
7
|
+
|
|
8
|
+
//#region src/lib/types.d.ts
|
|
9
|
+
type Awaitable<T> = T | Promise<T>;
|
|
10
|
+
/** allow content sources to implement interfaces for pages, instead of requiring consumers to specify manually */
|
|
11
|
+
interface Adapter<C extends ConfigContext = ConfigContext> {
|
|
12
|
+
"core:get-text"?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<string | undefined>;
|
|
13
|
+
"core:get-structured-data"?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<StructuredData | undefined>;
|
|
14
|
+
"core:render-body"?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<ReactNode>;
|
|
15
|
+
"core:render-toc"?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<TOCItemType[] | undefined>;
|
|
16
|
+
}
|
|
17
|
+
interface ServerPlugin<C extends ConfigContext = ConfigContext> {
|
|
18
|
+
/** receive & modify context */
|
|
19
|
+
init?: (this: AppContext<C>) => void;
|
|
20
|
+
createPages?: (this: AppContext<C>, fns: RouteFns) => Awaitable<void>;
|
|
21
|
+
}
|
|
22
|
+
type RouteFns = Parameters<Parameters<typeof createPages>[0]>[0] & {
|
|
23
|
+
createApiIsomorphic: (config: {
|
|
24
|
+
render: "static" | "dynamic";
|
|
25
|
+
path: string;
|
|
26
|
+
staticPaths?: string[][];
|
|
27
|
+
handler: (req: Request, ctx: {
|
|
28
|
+
params: Record<string, string | string[]>;
|
|
29
|
+
}) => Promise<Response>;
|
|
30
|
+
}) => void;
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { Adapter, Awaitable, RouteFns, ServerPlugin };
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import fsSync from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
//#region src/lib/vitefu.ts
|
|
6
|
+
let pnp;
|
|
7
|
+
let pnpWorkspaceLocators = [];
|
|
8
|
+
if (process.versions.pnp) try {
|
|
9
|
+
pnp = createRequire(import.meta.url)("pnpapi");
|
|
10
|
+
pnpWorkspaceLocators = pnp.getDependencyTreeRoots();
|
|
11
|
+
} catch {}
|
|
12
|
+
async function crawlFrameworkPkgs(options) {
|
|
13
|
+
const pkgJsonPath = await findClosestPkgJsonPath(options.root);
|
|
14
|
+
if (!pkgJsonPath) return {
|
|
15
|
+
optimizeDeps: {
|
|
16
|
+
include: [],
|
|
17
|
+
exclude: []
|
|
18
|
+
},
|
|
19
|
+
ssr: {
|
|
20
|
+
noExternal: [],
|
|
21
|
+
external: []
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const pkgJson = await readJson(pkgJsonPath).catch((e) => {
|
|
25
|
+
throw new Error(`Unable to read ${pkgJsonPath}`, { cause: e });
|
|
26
|
+
});
|
|
27
|
+
const optimizeDepsIncludeByPkgJsonPath = /* @__PURE__ */ new Map();
|
|
28
|
+
let optimizeDepsInclude = [];
|
|
29
|
+
let optimizeDepsExclude = [];
|
|
30
|
+
let ssrNoExternal = [];
|
|
31
|
+
let ssrExternal = [];
|
|
32
|
+
await crawl(pkgJsonPath, pkgJson);
|
|
33
|
+
optimizeDepsInclude = [...optimizeDepsIncludeByPkgJsonPath.values()];
|
|
34
|
+
if (options.viteUserConfig) {
|
|
35
|
+
const userOptimizeDepsExclude = options.viteUserConfig.optimizeDeps?.exclude;
|
|
36
|
+
if (userOptimizeDepsExclude) optimizeDepsInclude = optimizeDepsInclude.filter((dep) => !isDepExcluded(dep, userOptimizeDepsExclude));
|
|
37
|
+
const userOptimizeDepsInclude = options.viteUserConfig.optimizeDeps?.include;
|
|
38
|
+
if (userOptimizeDepsInclude) optimizeDepsExclude = optimizeDepsExclude.filter((dep) => !isDepIncluded(dep, userOptimizeDepsInclude));
|
|
39
|
+
const userSsrExternal = options.viteUserConfig.ssr?.external;
|
|
40
|
+
if (userSsrExternal) ssrNoExternal = ssrNoExternal.filter((dep) => !isDepExternaled(dep, userSsrExternal));
|
|
41
|
+
const userSsrNoExternal = options.viteUserConfig.ssr?.noExternal;
|
|
42
|
+
if (userSsrNoExternal) ssrExternal = ssrExternal.filter((dep) => !isDepNoExternaled(dep, userSsrNoExternal));
|
|
43
|
+
}
|
|
44
|
+
optimizeDepsInclude.sort();
|
|
45
|
+
optimizeDepsExclude.sort();
|
|
46
|
+
ssrExternal.sort();
|
|
47
|
+
ssrNoExternal.sort();
|
|
48
|
+
return {
|
|
49
|
+
optimizeDeps: {
|
|
50
|
+
include: optimizeDepsInclude,
|
|
51
|
+
exclude: optimizeDepsExclude
|
|
52
|
+
},
|
|
53
|
+
ssr: {
|
|
54
|
+
noExternal: ssrNoExternal,
|
|
55
|
+
external: ssrExternal
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
async function crawl(currentPkgJsonPath, currentPkgJson, parentDepNames = [], parentIsFrameworkPkg = false, hasFrameworkAncestor = false) {
|
|
59
|
+
const crawlDevDependencies = parentDepNames.length === 0 || isPrivateWorkspacePackage(currentPkgJsonPath, currentPkgJson, options.workspaceRoot);
|
|
60
|
+
const deps = [...Object.keys(currentPkgJson.dependencies ?? {}), ...crawlDevDependencies ? Object.keys(currentPkgJson.devDependencies ?? {}) : []].filter((dep) => !parentDepNames.includes(dep));
|
|
61
|
+
await Promise.all(deps.map(async (dep) => {
|
|
62
|
+
const frameworkByName = options.isFrameworkPkgByName?.(dep);
|
|
63
|
+
const semiFrameworkByName = options.isSemiFrameworkPkgByName?.(dep);
|
|
64
|
+
if (frameworkByName === false || semiFrameworkByName === false) return;
|
|
65
|
+
const depPkgJsonPath = await findDepPkgJsonPath(dep, currentPkgJsonPath, !!options.workspaceRoot);
|
|
66
|
+
if (!depPkgJsonPath) return;
|
|
67
|
+
const depPkgJson = await readJson(depPkgJsonPath).catch(() => {});
|
|
68
|
+
if (!depPkgJson) return;
|
|
69
|
+
const isFrameworkPkg = frameworkByName === true || options.isFrameworkPkgByJson?.(depPkgJson) === true;
|
|
70
|
+
const isSemiFrameworkPkg = semiFrameworkByName === true || options.isSemiFrameworkPkgByJson?.(depPkgJson) === true;
|
|
71
|
+
const depChain = parentDepNames.concat(dep);
|
|
72
|
+
if (isFrameworkPkg || isSemiFrameworkPkg) {
|
|
73
|
+
if (isFrameworkPkg) {
|
|
74
|
+
pushUnique(optimizeDepsExclude, dep);
|
|
75
|
+
pushUnique(ssrNoExternal, dep);
|
|
76
|
+
} else pushUnique(ssrNoExternal, dep);
|
|
77
|
+
await crawl(depPkgJsonPath, depPkgJson, depChain, true, true);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (!hasFrameworkAncestor) return;
|
|
81
|
+
if (await pkgNeedsOptimization(depPkgJson, depPkgJsonPath)) addOptimizedDep(depPkgJsonPath, depChain);
|
|
82
|
+
else await crawl(depPkgJsonPath, depPkgJson, depChain, false, true);
|
|
83
|
+
if (!options.isBuild && parentIsFrameworkPkg) pushUnique(ssrExternal, dep);
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
function addOptimizedDep(depPkgJsonPath, depChain) {
|
|
87
|
+
const includePath = depChain.join(" > ");
|
|
88
|
+
const current = optimizeDepsIncludeByPkgJsonPath.get(depPkgJsonPath);
|
|
89
|
+
if (!current || compareDepChains(includePath, current) < 0) optimizeDepsIncludeByPkgJsonPath.set(depPkgJsonPath, includePath);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function findClosestPkgJsonPath(dir, predicate) {
|
|
93
|
+
let currentDir = dir.endsWith("package.json") ? path.dirname(dir) : dir;
|
|
94
|
+
while (currentDir) {
|
|
95
|
+
const pkg = path.join(currentDir, "package.json");
|
|
96
|
+
try {
|
|
97
|
+
if ((await fs.stat(pkg)).isFile() && (!predicate || await predicate(pkg))) return pkg;
|
|
98
|
+
} catch {}
|
|
99
|
+
const nextDir = path.dirname(currentDir);
|
|
100
|
+
if (nextDir === currentDir) break;
|
|
101
|
+
currentDir = nextDir;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function pkgNeedsOptimization(pkgJson, pkgJsonPath) {
|
|
105
|
+
if (pkgJson.module || pkgJson.exports) return false;
|
|
106
|
+
if (pkgJson.main) {
|
|
107
|
+
const entryExt = path.extname(pkgJson.main);
|
|
108
|
+
return !entryExt || entryExt === ".js" || entryExt === ".cjs";
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
await fs.access(path.join(path.dirname(pkgJsonPath), "index.js"));
|
|
112
|
+
return true;
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async function findDepPkgJsonPath(dep, parent, usePnpWorkspaceLocators) {
|
|
118
|
+
if (pnp) {
|
|
119
|
+
if (usePnpWorkspaceLocators) try {
|
|
120
|
+
const locator = pnpWorkspaceLocators.find((root) => root.name === dep);
|
|
121
|
+
if (locator) {
|
|
122
|
+
const pkgPath = pnp.getPackageInformation(locator).packageLocation;
|
|
123
|
+
return path.resolve(pkgPath, "package.json");
|
|
124
|
+
}
|
|
125
|
+
} catch {}
|
|
126
|
+
try {
|
|
127
|
+
const depRoot = pnp.resolveToUnqualified(dep, parent);
|
|
128
|
+
if (!depRoot) return;
|
|
129
|
+
return path.join(depRoot, "package.json");
|
|
130
|
+
} catch {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
let root = parent;
|
|
135
|
+
while (root) {
|
|
136
|
+
const pkg = path.join(root, "node_modules", dep, "package.json");
|
|
137
|
+
try {
|
|
138
|
+
await fs.access(pkg);
|
|
139
|
+
return fsSync.realpathSync(pkg);
|
|
140
|
+
} catch {}
|
|
141
|
+
const nextRoot = path.dirname(root);
|
|
142
|
+
if (nextRoot === root) break;
|
|
143
|
+
root = nextRoot;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async function readJson(pkgJsonPath) {
|
|
147
|
+
return JSON.parse(await fs.readFile(pkgJsonPath, "utf8"));
|
|
148
|
+
}
|
|
149
|
+
function isPrivateWorkspacePackage(pkgJsonPath, pkgJson, workspaceRoot) {
|
|
150
|
+
return !!(workspaceRoot && pkgJson.private && !pkgJsonPath.match(/[/\\]node_modules[/\\]/) && !path.relative(workspaceRoot, pkgJsonPath).startsWith(".."));
|
|
151
|
+
}
|
|
152
|
+
function pushUnique(array, value) {
|
|
153
|
+
if (!array.includes(value)) array.push(value);
|
|
154
|
+
}
|
|
155
|
+
function compareDepChains(left, right) {
|
|
156
|
+
const leftDepth = left.split(" > ").length;
|
|
157
|
+
const rightDepth = right.split(" > ").length;
|
|
158
|
+
if (leftDepth !== rightDepth) return leftDepth - rightDepth;
|
|
159
|
+
return left.localeCompare(right);
|
|
160
|
+
}
|
|
161
|
+
function isDepIncluded(dep, optimizeDepsInclude) {
|
|
162
|
+
return optimizeDepsInclude.some((include) => dep === include);
|
|
163
|
+
}
|
|
164
|
+
function isDepExcluded(dep, optimizeDepsExclude) {
|
|
165
|
+
return optimizeDepsExclude.some((exclude) => dep === exclude || dep.startsWith(`${exclude} > `));
|
|
166
|
+
}
|
|
167
|
+
function isDepNoExternaled(dep, ssrNoExternal) {
|
|
168
|
+
if (typeof ssrNoExternal === "boolean") return ssrNoExternal;
|
|
169
|
+
return (Array.isArray(ssrNoExternal) ? ssrNoExternal : [ssrNoExternal]).some((noExternal) => {
|
|
170
|
+
if (typeof noExternal === "string") return dep === noExternal;
|
|
171
|
+
return noExternal.test(dep);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
function isDepExternaled(dep, ssrExternal) {
|
|
175
|
+
if (typeof ssrExternal === "boolean") return ssrExternal;
|
|
176
|
+
return ssrExternal.some((external) => dep === external);
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
export { crawlFrameworkPkgs };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AppContext } from "../lib/shared.js";
|
|
2
|
+
import { ConfigContext } from "../config.js";
|
|
3
|
+
import { Awaitable, ServerPlugin } from "../lib/types.js";
|
|
4
|
+
import { Index } from "fumadocs-core/search/flexsearch";
|
|
5
|
+
|
|
6
|
+
//#region src/plugins/flexsearch.d.ts
|
|
7
|
+
interface FlexsearchOptions<C extends ConfigContext = ConfigContext> {
|
|
8
|
+
buildIndex?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<Index>;
|
|
9
|
+
}
|
|
10
|
+
declare function flexsearchPlugin<C extends ConfigContext = ConfigContext>({
|
|
11
|
+
buildIndex
|
|
12
|
+
}?: FlexsearchOptions<NoInfer<C>>): ServerPlugin<C>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { FlexsearchOptions, flexsearchPlugin };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/plugins/flexsearch.ts
|
|
2
|
+
function flexsearchPlugin({ buildIndex = async function buildIndexDefault(page) {
|
|
3
|
+
for (const adapter of this.adapters) {
|
|
4
|
+
const structuredData = await adapter["core:get-structured-data"]?.call(this, page);
|
|
5
|
+
if (structuredData !== void 0) return {
|
|
6
|
+
id: page.url,
|
|
7
|
+
title: page.data.title ?? page.path,
|
|
8
|
+
description: page.data.description,
|
|
9
|
+
url: page.url,
|
|
10
|
+
structuredData
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
throw new Error("[Fumapress] Please specify the `buildIndex` option to flexsearchPlugin()");
|
|
14
|
+
} } = {}) {
|
|
15
|
+
return {
|
|
16
|
+
init() {
|
|
17
|
+
if (this.mode === "static") (this.data["core:provider"] ??= []).push(async (props) => {
|
|
18
|
+
props.search ??= {};
|
|
19
|
+
props.search.SearchDialog ??= (await import("../components/flexsearch-static.js")).default;
|
|
20
|
+
return props;
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
async createPages({ createApiIsomorphic }) {
|
|
24
|
+
const { flexsearchFromSource } = await import("fumadocs-core/search/flexsearch");
|
|
25
|
+
const server = flexsearchFromSource(this.getLoader, { buildIndex: buildIndex.bind(this) });
|
|
26
|
+
createApiIsomorphic({
|
|
27
|
+
render: this.mode === "static" ? "static" : "dynamic",
|
|
28
|
+
path: "/api/search",
|
|
29
|
+
handler: this.mode === "static" ? server.staticGET : server.GET
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { flexsearchPlugin };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AppContext } from "../lib/shared.js";
|
|
2
|
+
import { ConfigContext } from "../config.js";
|
|
3
|
+
import { Awaitable, ServerPlugin } from "../lib/types.js";
|
|
4
|
+
|
|
5
|
+
//#region src/plugins/llms.txt.d.ts
|
|
6
|
+
interface LLMsOptions<C extends ConfigContext = ConfigContext> {
|
|
7
|
+
getLLMText?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<string>;
|
|
8
|
+
}
|
|
9
|
+
declare function llmsPlugin<C extends ConfigContext = ConfigContext>(options?: LLMsOptions<NoInfer<C>>): ServerPlugin<C>;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { LLMsOptions, llmsPlugin };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { unstable_notFound } from "waku/router/server";
|
|
2
|
+
import { llms } from "fumadocs-core/source/llms";
|
|
3
|
+
//#region src/plugins/llms.txt.ts
|
|
4
|
+
function llmsPlugin(options = {}) {
|
|
5
|
+
const { getLLMText: _getLLMText = async function getLLMTextDefault(page) {
|
|
6
|
+
for (const adapter of this.adapters) {
|
|
7
|
+
const txt = await adapter["core:get-text"]?.call(this, page);
|
|
8
|
+
if (txt !== void 0) return `# ${page.data.title} (${page.url})\n\n${txt}`;
|
|
9
|
+
}
|
|
10
|
+
throw new Error("[Fumapress] Please specify the `getLLMText()` option in llmsPlugin()");
|
|
11
|
+
} } = options;
|
|
12
|
+
return {
|
|
13
|
+
init() {
|
|
14
|
+
this.data["core:docs-layout"] ??= {};
|
|
15
|
+
this.data["core:docs-layout"].renderers ??= [];
|
|
16
|
+
this.data["core:docs-layout"].renderers.push(function(res) {
|
|
17
|
+
res.markdownUrl ??= slugsToMarkdownPath(this.page.slugs, this.page.locale).url;
|
|
18
|
+
return res;
|
|
19
|
+
});
|
|
20
|
+
},
|
|
21
|
+
async createPages({ createApiIsomorphic }) {
|
|
22
|
+
const defaultRenderMode = this.mode === "dynamic" ? "dynamic" : "static";
|
|
23
|
+
const getLLMText = _getLLMText.bind(this);
|
|
24
|
+
createApiIsomorphic({
|
|
25
|
+
render: defaultRenderMode,
|
|
26
|
+
path: "/llms.txt",
|
|
27
|
+
handler: async () => {
|
|
28
|
+
const source = await this.getLoader();
|
|
29
|
+
return new Response(llms(source).index());
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
createApiIsomorphic({
|
|
33
|
+
render: defaultRenderMode,
|
|
34
|
+
path: "/llms-full.txt",
|
|
35
|
+
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"));
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
createApiIsomorphic({
|
|
42
|
+
render: defaultRenderMode,
|
|
43
|
+
path: this.i18nConfig ? "/[lang]/[...slugs]" : "/[...slugs]",
|
|
44
|
+
staticPaths: defaultRenderMode === "static" ? (await this.getLoader()).getPages().map((page) => slugsToMarkdownPath(page.slugs, page.locale).segments) : void 0,
|
|
45
|
+
handler: async (_req, { params }) => {
|
|
46
|
+
const page = (await this.getLoader()).getPage(markdownPathToSlugs(params.slugs), params.lang);
|
|
47
|
+
if (!page) unstable_notFound();
|
|
48
|
+
return new Response(await getLLMText(page), { headers: { "Content-Type": "text/markdown" } });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function markdownPathToSlugs(segs) {
|
|
55
|
+
const slugs = [...segs];
|
|
56
|
+
if (slugs.length === 0) return slugs;
|
|
57
|
+
slugs[slugs.length - 1] = slugs[slugs.length - 1].replace(/\.md$/, "");
|
|
58
|
+
if (slugs.length === 1 && slugs[0] === "index") slugs.pop();
|
|
59
|
+
return slugs;
|
|
60
|
+
}
|
|
61
|
+
function slugsToMarkdownPath(slugs, lang) {
|
|
62
|
+
const segments = [...slugs];
|
|
63
|
+
if (segments.length === 0) segments.push("index.md");
|
|
64
|
+
else segments[segments.length - 1] += ".md";
|
|
65
|
+
if (lang) segments.unshift(lang);
|
|
66
|
+
return {
|
|
67
|
+
segments,
|
|
68
|
+
url: `/${segments.join("/")}`
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
export { llmsPlugin };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AppContext } from "../lib/shared.js";
|
|
2
|
+
import { ConfigContext } from "../config.js";
|
|
3
|
+
import { Awaitable, ServerPlugin } from "../lib/types.js";
|
|
4
|
+
import { AdvancedIndex } from "fumadocs-core/search/server";
|
|
5
|
+
|
|
6
|
+
//#region src/plugins/orama-search.d.ts
|
|
7
|
+
interface OramaSearchOptions<C extends ConfigContext = ConfigContext> {
|
|
8
|
+
buildIndex?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<AdvancedIndex>;
|
|
9
|
+
}
|
|
10
|
+
declare function oramaSearchPlugin<C extends ConfigContext = ConfigContext>({
|
|
11
|
+
buildIndex
|
|
12
|
+
}?: OramaSearchOptions<NoInfer<C>>): ServerPlugin<C>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { OramaSearchOptions, oramaSearchPlugin };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/plugins/orama-search.ts
|
|
2
|
+
function oramaSearchPlugin({ buildIndex = async function buildIndexDefault(page) {
|
|
3
|
+
for (const adapter of this.adapters) {
|
|
4
|
+
const structuredData = await adapter["core:get-structured-data"]?.call(this, page);
|
|
5
|
+
if (structuredData !== void 0) return {
|
|
6
|
+
id: page.url,
|
|
7
|
+
title: page.data.title ?? page.path,
|
|
8
|
+
description: page.data.description,
|
|
9
|
+
url: page.url,
|
|
10
|
+
structuredData
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
throw new Error("[Fumapress] Please specify the `buildIndex` option to oramaSearchPlugin()");
|
|
14
|
+
} } = {}) {
|
|
15
|
+
return {
|
|
16
|
+
init() {
|
|
17
|
+
if (this.mode === "static") (this.data["core:provider"] ??= []).push(async (props) => {
|
|
18
|
+
props.search ??= {};
|
|
19
|
+
props.search.SearchDialog ??= (await import("../components/orama-search-static.js")).default;
|
|
20
|
+
return props;
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
async createPages({ createApiIsomorphic }) {
|
|
24
|
+
const { createFromSource } = await import("fumadocs-core/search/server");
|
|
25
|
+
const server = createFromSource(this.getLoader, { buildIndex: buildIndex.bind(this) });
|
|
26
|
+
createApiIsomorphic({
|
|
27
|
+
render: this.mode === "static" ? "static" : "dynamic",
|
|
28
|
+
path: "/api/search",
|
|
29
|
+
handler: this.mode === "static" ? server.staticGET : server.GET
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { oramaSearchPlugin };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AppContext } from "../lib/shared.js";
|
|
2
|
+
import { ConfigContext } from "../config.js";
|
|
3
|
+
import { Awaitable, ServerPlugin } from "../lib/types.js";
|
|
4
|
+
import { ReactNode } from "react";
|
|
5
|
+
import { ImageResponseOptions } from "@takumi-rs/image-response";
|
|
6
|
+
|
|
7
|
+
//#region src/plugins/takumi.d.ts
|
|
8
|
+
interface TakumiOptions<C extends ConfigContext = ConfigContext> {
|
|
9
|
+
generate?: (this: AppContext<C>, page: C["loaderConfig"]["page"]) => Awaitable<{
|
|
10
|
+
node: ReactNode;
|
|
11
|
+
options?: Partial<ImageResponseOptions>;
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
14
|
+
declare function takumiPlugin<C extends ConfigContext = ConfigContext>(options?: TakumiOptions<NoInfer<C>>): ServerPlugin<C>;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { TakumiOptions, takumiPlugin };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { unstable_notFound } from "waku/router/server";
|
|
3
|
+
import { ImageResponse } from "@takumi-rs/image-response";
|
|
4
|
+
//#region src/plugins/takumi.tsx
|
|
5
|
+
function takumiPlugin(options = {}) {
|
|
6
|
+
const { generate = async function generateDefault(page) {
|
|
7
|
+
const { generate } = await import("fumadocs-ui/og/takumi");
|
|
8
|
+
return { node: generate({
|
|
9
|
+
title: page.data.title,
|
|
10
|
+
description: page.data.description,
|
|
11
|
+
site: this.siteConfig.name
|
|
12
|
+
}) };
|
|
13
|
+
} } = options;
|
|
14
|
+
const width = 1200;
|
|
15
|
+
const height = 630;
|
|
16
|
+
return {
|
|
17
|
+
init() {
|
|
18
|
+
(this.data["core:page-meta"] ??= []).push((page) => {
|
|
19
|
+
const pathname = slugsToImagePath(page.slugs, page.locale).url;
|
|
20
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21
|
+
/* @__PURE__ */ jsx("meta", {
|
|
22
|
+
property: "og:image",
|
|
23
|
+
content: this.siteConfig.baseUrl ? new URL(pathname, this.siteConfig.baseUrl).href : pathname
|
|
24
|
+
}),
|
|
25
|
+
/* @__PURE__ */ jsx("meta", {
|
|
26
|
+
property: "og:image:width",
|
|
27
|
+
content: `${width}`
|
|
28
|
+
}),
|
|
29
|
+
/* @__PURE__ */ jsx("meta", {
|
|
30
|
+
property: "og:image:height",
|
|
31
|
+
content: `${height}`
|
|
32
|
+
}),
|
|
33
|
+
/* @__PURE__ */ jsx("meta", {
|
|
34
|
+
property: "twitter:card",
|
|
35
|
+
content: "summary_large_image"
|
|
36
|
+
})
|
|
37
|
+
] });
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
async createPages({ createApiIsomorphic }) {
|
|
41
|
+
const renderMode = this.mode === "dynamic" ? "dynamic" : "static";
|
|
42
|
+
createApiIsomorphic({
|
|
43
|
+
render: renderMode,
|
|
44
|
+
path: this.i18nConfig ? "/[lang]/[...slugs]" : "/[...slugs]",
|
|
45
|
+
staticPaths: renderMode === "static" ? (await this.getLoader()).getPages().map((page) => slugsToImagePath(page.slugs, page.locale).segments) : void 0,
|
|
46
|
+
handler: async (_, { params }) => {
|
|
47
|
+
const page = (await this.getLoader()).getPage(imagePathToSlugs(params.slugs), params.lang);
|
|
48
|
+
if (!page) unstable_notFound();
|
|
49
|
+
const { node, options } = await generate.call(this, page);
|
|
50
|
+
return new ImageResponse(node, {
|
|
51
|
+
width,
|
|
52
|
+
height,
|
|
53
|
+
...options,
|
|
54
|
+
format: "webp"
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function slugsToImagePath(slugs, lang) {
|
|
62
|
+
const segments = [...slugs];
|
|
63
|
+
if (segments.length === 0) segments.push("index.webp");
|
|
64
|
+
else segments[segments.length - 1] += ".webp";
|
|
65
|
+
if (lang) segments.unshift(lang);
|
|
66
|
+
return {
|
|
67
|
+
segments,
|
|
68
|
+
url: `/${segments.join("/")}`
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function imagePathToSlugs(segs) {
|
|
72
|
+
const slugs = [...segs];
|
|
73
|
+
if (slugs.length === 0) return slugs;
|
|
74
|
+
slugs[slugs.length - 1] = slugs[slugs.length - 1].replace(/\.webp$/, "");
|
|
75
|
+
if (slugs.length === 1 && slugs[0] === "index") slugs.pop();
|
|
76
|
+
return slugs;
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
export { takumiPlugin };
|