fumadocs-core 8.0.0

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.
@@ -0,0 +1,209 @@
1
+ import {
2
+ __spreadProps,
3
+ __spreadValues
4
+ } from "../chunk-WEAGW6MQ.js";
5
+
6
+ // src/search/server.ts
7
+ import { Document } from "flexsearch";
8
+ import nextLib from "next/server";
9
+ function createSearchAPI(type, options) {
10
+ if (type === "simple") {
11
+ return initSearchAPI(options);
12
+ }
13
+ return initSearchAPIAdvanced(options);
14
+ }
15
+ function createI18nSearchAPI(type, options) {
16
+ const map = /* @__PURE__ */ new Map();
17
+ for (const [k, v] of options.indexes) {
18
+ map.set(
19
+ k,
20
+ createSearchAPI(type, __spreadProps(__spreadValues({}, options), {
21
+ language: k,
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment -- Avoid complicated types
23
+ indexes: v
24
+ }))
25
+ );
26
+ }
27
+ return {
28
+ GET(request) {
29
+ const locale = request.nextUrl.searchParams.get("locale");
30
+ if (locale) {
31
+ const handler = map.get(locale);
32
+ if (handler)
33
+ return handler.GET(request);
34
+ }
35
+ return nextLib.NextResponse.json([]);
36
+ }
37
+ };
38
+ }
39
+ function initSearchAPI({ indexes, language }) {
40
+ const store = ["title", "url"];
41
+ const index = new Document({
42
+ language,
43
+ optimize: true,
44
+ cache: 100,
45
+ document: {
46
+ id: "url",
47
+ store,
48
+ index: [
49
+ {
50
+ field: "title",
51
+ tokenize: "forward",
52
+ resolution: 9
53
+ },
54
+ {
55
+ field: "content",
56
+ tokenize: "strict",
57
+ context: {
58
+ depth: 1,
59
+ resolution: 9
60
+ }
61
+ },
62
+ {
63
+ field: "keywords",
64
+ tokenize: "strict",
65
+ resolution: 9
66
+ }
67
+ ]
68
+ }
69
+ });
70
+ for (const page of indexes) {
71
+ index.add({
72
+ title: page.title,
73
+ url: page.url,
74
+ content: page.content,
75
+ keywords: page.keywords
76
+ });
77
+ }
78
+ return {
79
+ GET(request) {
80
+ const { searchParams } = request.nextUrl;
81
+ const query = searchParams.get("query");
82
+ if (!query)
83
+ return nextLib.NextResponse.json([]);
84
+ const results = index.search(query, 5, {
85
+ enrich: true,
86
+ suggest: true
87
+ });
88
+ if (results.length === 0)
89
+ return nextLib.NextResponse.json([]);
90
+ const pages = results[0].result.map((page) => ({
91
+ type: "page",
92
+ content: page.doc.title,
93
+ id: page.doc.url,
94
+ url: page.doc.url
95
+ }));
96
+ return nextLib.NextResponse.json(pages);
97
+ }
98
+ };
99
+ }
100
+ function initSearchAPIAdvanced({
101
+ indexes,
102
+ language,
103
+ tag = false
104
+ }) {
105
+ const store = ["id", "url", "content", "page_id", "type"];
106
+ const index = new Document({
107
+ language,
108
+ cache: 100,
109
+ tokenize: "forward",
110
+ optimize: true,
111
+ context: {
112
+ depth: 2,
113
+ bidirectional: true,
114
+ resolution: 9
115
+ },
116
+ document: {
117
+ id: "id",
118
+ tag: tag ? "tag" : void 0,
119
+ store,
120
+ index: ["content"]
121
+ }
122
+ });
123
+ for (const page of indexes) {
124
+ const data = page.structuredData;
125
+ let id = 0;
126
+ index.add({
127
+ id: page.id,
128
+ page_id: page.id,
129
+ type: "page",
130
+ content: page.title,
131
+ tag: page.tag,
132
+ url: page.url
133
+ });
134
+ for (const heading of data.headings) {
135
+ index.add({
136
+ id: page.id + id++,
137
+ page_id: page.id,
138
+ type: "heading",
139
+ tag: page.tag,
140
+ url: `${page.url}#${heading.id}`,
141
+ content: heading.content
142
+ });
143
+ }
144
+ for (const content of data.contents) {
145
+ index.add({
146
+ id: page.id + id++,
147
+ page_id: page.id,
148
+ tag: page.tag,
149
+ type: "text",
150
+ url: content.heading ? `${page.url}#${content.heading}` : page.url,
151
+ content: content.content
152
+ });
153
+ }
154
+ }
155
+ return {
156
+ GET(request) {
157
+ var _a, _b, _c;
158
+ const query = request.nextUrl.searchParams.get("query");
159
+ const paramTag = request.nextUrl.searchParams.get("tag");
160
+ if (!query)
161
+ return nextLib.NextResponse.json([]);
162
+ const results = index.search(query, 5, {
163
+ enrich: true,
164
+ tag: paramTag != null ? paramTag : void 0,
165
+ limit: 6
166
+ });
167
+ const map = /* @__PURE__ */ new Map();
168
+ const sortedResult = [];
169
+ for (const item of (_b = (_a = results[0]) == null ? void 0 : _a.result) != null ? _b : []) {
170
+ if (item.doc.type === "page") {
171
+ if (!map.has(item.doc.page_id)) {
172
+ map.set(item.doc.page_id, []);
173
+ }
174
+ continue;
175
+ }
176
+ const i = {
177
+ id: item.doc.id,
178
+ content: item.doc.content,
179
+ type: item.doc.type,
180
+ url: item.doc.url
181
+ };
182
+ if (map.has(item.doc.page_id)) {
183
+ (_c = map.get(item.doc.page_id)) == null ? void 0 : _c.push(i);
184
+ } else {
185
+ map.set(item.doc.page_id, [i]);
186
+ }
187
+ }
188
+ for (const [id, items] of map.entries()) {
189
+ const page = index.get(id);
190
+ if (!page)
191
+ continue;
192
+ sortedResult.push({
193
+ id: page.id,
194
+ content: page.content,
195
+ type: "page",
196
+ url: page.url
197
+ });
198
+ sortedResult.push(...items);
199
+ }
200
+ return nextLib.NextResponse.json(sortedResult);
201
+ }
202
+ };
203
+ }
204
+ export {
205
+ createI18nSearchAPI,
206
+ createSearchAPI,
207
+ initSearchAPI,
208
+ initSearchAPIAdvanced
209
+ };
@@ -0,0 +1,8 @@
1
+ interface SortedResult {
2
+ id: string;
3
+ url: string;
4
+ type: 'page' | 'heading' | 'text';
5
+ content: string;
6
+ }
7
+
8
+ export type { SortedResult };
File without changes
@@ -0,0 +1,28 @@
1
+ import { SearchOptions, Hit } from '@algolia/client-search';
2
+ import { SearchIndex } from 'algoliasearch/lite';
3
+ import { SWRResponse } from 'swr';
4
+ import { SortedResult } from '../search/shared.js';
5
+ import { BaseIndex } from './server.js';
6
+ import 'algoliasearch';
7
+ import '../remark-structure-RwYPDA6M.js';
8
+ import 'mdast';
9
+ import 'unified';
10
+
11
+ interface Options extends SearchOptions {
12
+ /**
13
+ * Use `empty` as result if query is empty
14
+ */
15
+ allowEmpty?: boolean;
16
+ }
17
+ declare function groupResults(hits: Hit<BaseIndex>[]): SortedResult[];
18
+ declare function searchDocs(index: SearchIndex, query: string, options?: SearchOptions): Promise<SortedResult[]>;
19
+ interface UseAlgoliaSearch {
20
+ search: string;
21
+ setSearch: (v: string) => void;
22
+ query: SWRResponse<SortedResult[] | 'empty', Error, {
23
+ keepPreviousData: true;
24
+ }>;
25
+ }
26
+ declare function useAlgoliaSearch(index: SearchIndex, { allowEmpty, ...options }?: Options): UseAlgoliaSearch;
27
+
28
+ export { type Options, groupResults, searchDocs, useAlgoliaSearch };
@@ -0,0 +1,68 @@
1
+ import {
2
+ __async,
3
+ __objRest,
4
+ __spreadValues
5
+ } from "../chunk-WEAGW6MQ.js";
6
+
7
+ // src/search-algolia/client.ts
8
+ import { useState } from "react";
9
+ import useSWR from "swr";
10
+ function groupResults(hits) {
11
+ const grouped = [];
12
+ const scannedUrls = /* @__PURE__ */ new Set();
13
+ for (const hit of hits) {
14
+ if (!scannedUrls.has(hit.url)) {
15
+ scannedUrls.add(hit.url);
16
+ grouped.push({
17
+ id: hit.url,
18
+ type: "page",
19
+ url: hit.url,
20
+ content: hit.title
21
+ });
22
+ }
23
+ grouped.push({
24
+ id: hit.objectID,
25
+ type: hit.content === hit.section ? "heading" : "text",
26
+ url: hit.section_id ? `${hit.url}#${hit.section_id}` : hit.url,
27
+ content: hit.content
28
+ });
29
+ }
30
+ return grouped;
31
+ }
32
+ function searchDocs(index, query, options) {
33
+ return __async(this, null, function* () {
34
+ if (query.length === 0) {
35
+ const result2 = yield index.search(query, __spreadValues({
36
+ distinct: 1,
37
+ hitsPerPage: 8
38
+ }, options));
39
+ return groupResults(result2.hits).filter((hit) => hit.type === "page");
40
+ }
41
+ const result = yield index.search(query, __spreadValues({
42
+ distinct: 5,
43
+ hitsPerPage: 10
44
+ }, options));
45
+ return groupResults(result.hits);
46
+ });
47
+ }
48
+ function useAlgoliaSearch(index, _a = {}) {
49
+ var _b = _a, { allowEmpty = true } = _b, options = __objRest(_b, ["allowEmpty"]);
50
+ const [search, setSearch] = useState("");
51
+ const query = useSWR(
52
+ ["/api/search", search, allowEmpty, options],
53
+ () => __async(this, null, function* () {
54
+ if (allowEmpty && search.length === 0)
55
+ return "empty";
56
+ return searchDocs(index, search, options);
57
+ }),
58
+ {
59
+ keepPreviousData: true
60
+ }
61
+ );
62
+ return { search, setSearch, query };
63
+ }
64
+ export {
65
+ groupResults,
66
+ searchDocs,
67
+ useAlgoliaSearch
68
+ };
@@ -0,0 +1,45 @@
1
+ import { SearchClient, SearchIndex } from 'algoliasearch';
2
+ import { S as StructuredData } from '../remark-structure-RwYPDA6M.js';
3
+ import 'mdast';
4
+ import 'unified';
5
+
6
+ interface DocumentRecord {
7
+ /**
8
+ * The ID of document, must be unique
9
+ */
10
+ _id: string;
11
+ title: string;
12
+ /**
13
+ * URL to the page
14
+ */
15
+ url: string;
16
+ structured: StructuredData;
17
+ /**
18
+ * Data to be added to each section index
19
+ */
20
+ extra_data?: object;
21
+ }
22
+ interface SyncOptions {
23
+ document?: string;
24
+ documents: DocumentRecord[];
25
+ }
26
+ declare function sync(client: SearchClient, { document, documents }: SyncOptions): Promise<void>;
27
+ declare function setIndexSettings(index: SearchIndex): Promise<void>;
28
+ declare function updateDocuments(index: SearchIndex, documents: DocumentRecord[]): Promise<void>;
29
+ interface BaseIndex {
30
+ objectID: string;
31
+ title: string;
32
+ url: string;
33
+ section?: string;
34
+ /**
35
+ * The anchor id
36
+ */
37
+ section_id?: string;
38
+ /**
39
+ * The id of page, used for distinct
40
+ */
41
+ page_id: string;
42
+ content: string;
43
+ }
44
+
45
+ export { type BaseIndex, setIndexSettings, sync, updateDocuments };
@@ -0,0 +1,68 @@
1
+ import {
2
+ __async,
3
+ __spreadValues
4
+ } from "../chunk-WEAGW6MQ.js";
5
+
6
+ // src/search-algolia/server.ts
7
+ import { randomUUID } from "crypto";
8
+ function sync(_0, _1) {
9
+ return __async(this, arguments, function* (client, { document = "document", documents }) {
10
+ const index = client.initIndex(document);
11
+ yield setIndexSettings(index);
12
+ yield updateDocuments(index, documents);
13
+ });
14
+ }
15
+ function setIndexSettings(index) {
16
+ return __async(this, null, function* () {
17
+ yield index.setSettings({
18
+ attributeForDistinct: "page_id",
19
+ attributesToRetrieve: ["title", "section", "content", "url", "section_id"],
20
+ searchableAttributes: ["title", "section", "content"],
21
+ attributesToSnippet: [],
22
+ attributesForFaceting: ["tag"]
23
+ });
24
+ });
25
+ }
26
+ function getSections(page) {
27
+ const scannedHeadings = /* @__PURE__ */ new Set();
28
+ return page.structured.contents.flatMap((p) => {
29
+ const heading = p.heading ? page.structured.headings.find((h) => p.heading === h.id) : null;
30
+ const section = {
31
+ section: heading == null ? void 0 : heading.content,
32
+ section_id: heading == null ? void 0 : heading.id,
33
+ content: p.content
34
+ };
35
+ if (heading && !scannedHeadings.has(heading.id)) {
36
+ scannedHeadings.add(heading.id);
37
+ return [
38
+ {
39
+ section: heading.content,
40
+ section_id: heading.id,
41
+ content: heading.content
42
+ },
43
+ section
44
+ ];
45
+ }
46
+ return section;
47
+ });
48
+ }
49
+ function updateDocuments(index, documents) {
50
+ return __async(this, null, function* () {
51
+ const objects = documents.flatMap((page) => {
52
+ return getSections(page).map(
53
+ (section) => __spreadValues(__spreadValues({
54
+ objectID: `${page._id}-${randomUUID()}`,
55
+ title: page.title,
56
+ url: page.url,
57
+ page_id: page._id
58
+ }, section), page.extra_data)
59
+ );
60
+ });
61
+ yield index.replaceAllObjects(objects);
62
+ });
63
+ }
64
+ export {
65
+ setIndexSettings,
66
+ sync,
67
+ updateDocuments
68
+ };
@@ -0,0 +1,57 @@
1
+ import { T as TableOfContents } from '../get-toc-YF_TdazL.js';
2
+ export { a as TOCItemType, g as getTableOfContents } from '../get-toc-YF_TdazL.js';
3
+ import { N as Node, I as Item, R as Root } from '../page-tree-izSPERQk.js';
4
+ export { p as PageTree } from '../page-tree-izSPERQk.js';
5
+ import 'react';
6
+
7
+ /**
8
+ * Parse TOC from portable text (Sanity)
9
+ *
10
+ * @param value - Blocks
11
+ * @param slugFn - A function that generates slug from title
12
+ */
13
+ declare function getTableOfContentsFromPortableText(value: any): TableOfContents;
14
+
15
+ /**
16
+ * Flatten tree to an array of page nodes
17
+ */
18
+ declare function flattenTree(tree: Node[]): Item[];
19
+ /**
20
+ * Get neighbours of a page, useful for implementing "previous & next" buttons
21
+ */
22
+ declare function findNeighbour(tree: Root, url: string): {
23
+ previous?: Item;
24
+ next?: Item;
25
+ };
26
+ /**
27
+ * Separate the folder nodes of a root into multiple roots
28
+ */
29
+ declare function separatePageTree(pageTree: Root): Root[];
30
+
31
+ interface GetGithubLastCommitOptions {
32
+ /**
33
+ * Repository name, like "next-docs"
34
+ */
35
+ repo: string;
36
+ /** Owner of repository */
37
+ owner: string;
38
+ /**
39
+ * Path to file
40
+ */
41
+ path: string;
42
+ /**
43
+ * Github access token
44
+ */
45
+ token?: string;
46
+ /**
47
+ * Custom query parameters
48
+ */
49
+ params?: Record<string, string>;
50
+ options?: RequestInit;
51
+ }
52
+ /**
53
+ * Get the last edit time of a file
54
+ */
55
+ declare function getGithubLastEdit({ repo, token, owner, path, options, params: customParams, }: GetGithubLastCommitOptions): Promise<Date | null>;
56
+
57
+ export { type GetGithubLastCommitOptions, TableOfContents, findNeighbour, flattenTree, getGithubLastEdit, getTableOfContentsFromPortableText, separatePageTree };
@@ -0,0 +1,148 @@
1
+ import {
2
+ remarkHeading
3
+ } from "../chunk-GT3Y35O6.js";
4
+ import {
5
+ __async
6
+ } from "../chunk-WEAGW6MQ.js";
7
+
8
+ // src/server/get-toc.ts
9
+ import { remark } from "remark";
10
+ function getTableOfContents(content) {
11
+ return __async(this, null, function* () {
12
+ const result = yield remark().use(remarkHeading).process(content);
13
+ if ("toc" in result.data)
14
+ return result.data.toc;
15
+ return [];
16
+ });
17
+ }
18
+
19
+ // src/server/get-toc-sanity.ts
20
+ import Slugger from "github-slugger";
21
+ var slugger = new Slugger();
22
+ function getTableOfContentsFromPortableText(value) {
23
+ if (!Array.isArray(value)) {
24
+ throw new Error("Invalid body type");
25
+ }
26
+ slugger.reset();
27
+ const result = [];
28
+ for (const block of value) {
29
+ dfs(block, result);
30
+ }
31
+ return result;
32
+ }
33
+ function dfs(block, list) {
34
+ var _a;
35
+ if (block.style && block.style.length === 2 && block.style.startsWith("h")) {
36
+ const depth = Number(block.style[1]);
37
+ if (Number.isNaN(depth))
38
+ return;
39
+ const text = flattenNode(block);
40
+ list.push({
41
+ title: text,
42
+ url: slugger.slug(text),
43
+ depth
44
+ });
45
+ return;
46
+ }
47
+ (_a = block.children) == null ? void 0 : _a.forEach((child) => {
48
+ dfs(child, list);
49
+ });
50
+ }
51
+ function flattenNode(block) {
52
+ var _a, _b;
53
+ let text = "";
54
+ if (block._type === "span") {
55
+ return (_a = block.text) != null ? _a : "";
56
+ }
57
+ (_b = block.children) == null ? void 0 : _b.forEach((child) => {
58
+ text += flattenNode(child);
59
+ });
60
+ return text;
61
+ }
62
+
63
+ // src/server/page-tree-utils.ts
64
+ function flattenTree(tree) {
65
+ return tree.flatMap((node) => {
66
+ if (node.type === "separator")
67
+ return [];
68
+ if (node.type === "folder") {
69
+ const child = flattenTree(node.children);
70
+ if (node.index)
71
+ return [node.index, ...child];
72
+ return child;
73
+ }
74
+ return [node];
75
+ });
76
+ }
77
+ function findNeighbour(tree, url) {
78
+ const list = flattenTree(tree.children);
79
+ for (let i = 0; i < list.length; i++) {
80
+ if (list[i].url === url) {
81
+ return {
82
+ next: list[i + 1],
83
+ previous: list[i - 1]
84
+ };
85
+ }
86
+ }
87
+ return {};
88
+ }
89
+ function separatePageTree(pageTree) {
90
+ return pageTree.children.flatMap((child) => {
91
+ var _a;
92
+ if (child.type !== "folder")
93
+ return [];
94
+ return {
95
+ name: child.name,
96
+ url: (_a = child.index) == null ? void 0 : _a.url,
97
+ children: child.children
98
+ };
99
+ });
100
+ }
101
+
102
+ // src/server/page-tree.ts
103
+ var page_tree_exports = {};
104
+
105
+ // src/server/git-api.ts
106
+ function getGithubLastEdit(_0) {
107
+ return __async(this, arguments, function* ({
108
+ repo,
109
+ token,
110
+ owner,
111
+ path,
112
+ options = {},
113
+ params: customParams = {}
114
+ }) {
115
+ const params = new URLSearchParams();
116
+ params.set("path", path);
117
+ params.set("page", "1");
118
+ params.set("per_page", "1");
119
+ for (const [key, value] of Object.entries(customParams)) {
120
+ params.set(key, value);
121
+ }
122
+ if (token) {
123
+ options.headers = new Headers(options.headers);
124
+ options.headers.append("authorization", token);
125
+ }
126
+ const res = yield fetch(
127
+ `https://api.github.com/repos/${owner}/${repo}/commits?${params.toString()}`,
128
+ options
129
+ );
130
+ if (!res.ok)
131
+ throw new Error(
132
+ `Failed to fetch last edit time from Git ${yield res.text()}`
133
+ );
134
+ const data = yield res.json();
135
+ if (data.length === 0)
136
+ return null;
137
+ return new Date(data[0].commit.committer.date);
138
+ });
139
+ }
140
+ export {
141
+ page_tree_exports as PageTree,
142
+ findNeighbour,
143
+ flattenTree,
144
+ getGithubLastEdit,
145
+ getTableOfContents,
146
+ getTableOfContentsFromPortableText,
147
+ separatePageTree
148
+ };
@@ -0,0 +1,19 @@
1
+ import { ReactNode, ElementType, ComponentPropsWithoutRef } from 'react';
2
+
3
+ interface SidebarProviderProps {
4
+ open?: boolean;
5
+ onOpenChange?: (v: boolean) => void;
6
+ children: ReactNode;
7
+ }
8
+ declare function SidebarProvider(props: SidebarProviderProps): JSX.Element;
9
+ type WithAs<T extends ElementType, Extend = object> = Omit<ComponentPropsWithoutRef<T>, 'as' | keyof Extend> & Extend & {
10
+ as?: T;
11
+ };
12
+ type SidebarTriggerProps<T extends ElementType> = WithAs<T>;
13
+ declare function SidebarTrigger<T extends ElementType = 'button'>({ as, ...props }: SidebarTriggerProps<T>): JSX.Element;
14
+ type SidebarContentProps<T extends ElementType> = WithAs<T, {
15
+ minWidth?: number;
16
+ }>;
17
+ declare function SidebarList<T extends ElementType = 'aside'>({ as, minWidth, ...props }: SidebarContentProps<T>): JSX.Element;
18
+
19
+ export { type SidebarContentProps, SidebarList, SidebarProvider, type SidebarProviderProps, SidebarTrigger, type SidebarTriggerProps };