mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
Core & OpenAPI: Improve Fumadocs OpenAPI support (TOC & Structured Data plugins)
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'fumadocs-openapi': patch
|
||||
'fumadocs-core': patch
|
||||
'fumadocs-mdx': patch
|
||||
---
|
||||
|
||||
Improve Fumadocs OpenAPI support
|
||||
@@ -34,5 +34,3 @@ curl -XPOST "https://api.unkey.dev/v1/keys.createKey" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"apiId": "api_123", "name": "My Key"}'
|
||||
```
|
||||
|
||||

|
||||
|
||||
@@ -40,6 +40,19 @@ export function remarkHeading({
|
||||
const toc: TOCItemType[] = [];
|
||||
slugger.reset();
|
||||
|
||||
// Fumadocs OpenAPI Generated TOC
|
||||
if (file.data.frontmatter) {
|
||||
const frontmatter = file.data.frontmatter as {
|
||||
_openapi?: {
|
||||
toc?: TOCItemType[];
|
||||
};
|
||||
};
|
||||
|
||||
if (frontmatter._openapi?.toc) {
|
||||
toc.push(...frontmatter._openapi.toc);
|
||||
}
|
||||
}
|
||||
|
||||
visit(root, 'heading', (heading) => {
|
||||
heading.data ||= {};
|
||||
heading.data.hProperties ||= {};
|
||||
|
||||
@@ -45,6 +45,20 @@ export function remarkStructure({
|
||||
const data: StructuredData = { contents: [], headings: [] };
|
||||
let lastHeading: string | undefined = '';
|
||||
|
||||
// Fumadocs OpenAPI Generated Structured Data
|
||||
if (file.data.frontmatter) {
|
||||
const frontmatter = file.data.frontmatter as {
|
||||
_openapi?: {
|
||||
structuredData?: StructuredData;
|
||||
};
|
||||
};
|
||||
|
||||
if (frontmatter._openapi?.structuredData) {
|
||||
data.headings.push(...frontmatter._openapi.structuredData.headings);
|
||||
data.contents.push(...frontmatter._openapi.structuredData.contents);
|
||||
}
|
||||
}
|
||||
|
||||
visit(node, types, (element) => {
|
||||
if (element.type === 'root') return;
|
||||
const content = flattenNode(element).trim();
|
||||
|
||||
@@ -18,8 +18,6 @@ export const attachFile: BuildPageTreeOptions['attachFile'] = (node, file) => {
|
||||
};
|
||||
|
||||
method = meta.method;
|
||||
} else if ('method' in data && typeof data.method === 'string') {
|
||||
method = data.method;
|
||||
}
|
||||
|
||||
if (method) {
|
||||
|
||||
@@ -64,7 +64,7 @@ export function generateDocument(
|
||||
out.push(imports);
|
||||
}
|
||||
|
||||
out.push(pageContent(data, options.page));
|
||||
out.push(pageContent(options.page));
|
||||
|
||||
return out.join('\n\n');
|
||||
}
|
||||
@@ -108,24 +108,6 @@ function generateStaticData(
|
||||
return { toc, structuredData };
|
||||
}
|
||||
|
||||
function pageContent(data: StaticData, props: ApiPageProps): string {
|
||||
// modify toc and structured data if possible
|
||||
// it may not be compatible with other content sources except Fumadocs MDX
|
||||
// TODO: Maybe add to frontmatter and let developers to handle them?
|
||||
return `<APIPage document={${JSON.stringify(props.document)}} operations={${JSON.stringify(props.operations)}} hasHead={${JSON.stringify(props.hasHead)}} />
|
||||
|
||||
export function startup() {
|
||||
if (typeof toc !== 'undefined') {
|
||||
// toc might be immutable
|
||||
while (toc.length > 0) toc.pop()
|
||||
toc.push(...${JSON.stringify(data.toc)})
|
||||
}
|
||||
|
||||
if (typeof structuredData !== 'undefined') {
|
||||
structuredData.headings = ${JSON.stringify(data.structuredData.headings)}
|
||||
structuredData.contents = ${JSON.stringify(data.structuredData.contents)}
|
||||
}
|
||||
}
|
||||
|
||||
{startup()}`;
|
||||
function pageContent(props: ApiPageProps): string {
|
||||
return `<APIPage document={${JSON.stringify(props.document)}} operations={${JSON.stringify(props.operations)}} hasHead={${JSON.stringify(props.hasHead)}} />`;
|
||||
}
|
||||
|
||||
@@ -46,19 +46,4 @@ _openapi:
|
||||
heading: delete-special-event
|
||||
---
|
||||
|
||||
<APIPage document={"./fixtures/museum.yaml"} operations={[{"method":"get","path":"/special-events"},{"method":"post","path":"/special-events"},{"method":"get","path":"/special-events/{eventId}"},{"method":"patch","path":"/special-events/{eventId}"},{"method":"delete","path":"/special-events/{eventId}"}]} hasHead={true} />
|
||||
|
||||
export function startup() {
|
||||
if (typeof toc !== 'undefined') {
|
||||
// toc might be immutable
|
||||
while (toc.length > 0) toc.pop()
|
||||
toc.push(...[{"depth":2,"title":"List special events","url":"#list-special-events"},{"depth":2,"title":"Create special events","url":"#create-special-events"},{"depth":2,"title":"Get special event","url":"#get-special-event"},{"depth":2,"title":"Update special event","url":"#update-special-event"},{"depth":2,"title":"Delete special event","url":"#delete-special-event"}])
|
||||
}
|
||||
|
||||
if (typeof structuredData !== 'undefined') {
|
||||
structuredData.headings = [{"content":"List special events","id":"list-special-events"},{"content":"Create special events","id":"create-special-events"},{"content":"Get special event","id":"get-special-event"},{"content":"Update special event","id":"update-special-event"},{"content":"Delete special event","id":"delete-special-event"}]
|
||||
structuredData.contents = [{"content":"Return a list of upcoming special events at the museum.","heading":"list-special-events"},{"content":"Creates a new special event for the museum.","heading":"create-special-events"},{"content":"Get details about a special event.","heading":"get-special-event"},{"content":"Update the details of a special event","heading":"update-special-event"},{"content":"Delete a special event from the collection. Allows museum to cancel planned events.","heading":"delete-special-event"}]
|
||||
}
|
||||
}
|
||||
|
||||
{startup()}
|
||||
<APIPage document={"./fixtures/museum.yaml"} operations={[{"method":"get","path":"/special-events"},{"method":"post","path":"/special-events"},{"method":"get","path":"/special-events/{eventId}"},{"method":"patch","path":"/special-events/{eventId}"},{"method":"delete","path":"/special-events/{eventId}"}]} hasHead={true} />
|
||||
@@ -16,19 +16,4 @@ _openapi:
|
||||
heading: get-museum-hours
|
||||
---
|
||||
|
||||
<APIPage document={"./fixtures/museum.yaml"} operations={[{"method":"get","path":"/museum-hours"}]} hasHead={true} />
|
||||
|
||||
export function startup() {
|
||||
if (typeof toc !== 'undefined') {
|
||||
// toc might be immutable
|
||||
while (toc.length > 0) toc.pop()
|
||||
toc.push(...[{"depth":2,"title":"Get museum hours","url":"#get-museum-hours"}])
|
||||
}
|
||||
|
||||
if (typeof structuredData !== 'undefined') {
|
||||
structuredData.headings = [{"content":"Get museum hours","id":"get-museum-hours"}]
|
||||
structuredData.contents = [{"content":"Get upcoming museum operating hours","heading":"get-museum-hours"}]
|
||||
}
|
||||
}
|
||||
|
||||
{startup()}
|
||||
<APIPage document={"./fixtures/museum.yaml"} operations={[{"method":"get","path":"/museum-hours"}]} hasHead={true} />
|
||||
@@ -25,19 +25,4 @@ _openapi:
|
||||
heading: get-ticket-qr-code
|
||||
---
|
||||
|
||||
<APIPage document={"./fixtures/museum.yaml"} operations={[{"method":"post","path":"/tickets"},{"method":"get","path":"/tickets/{ticketId}/qr"}]} hasHead={true} />
|
||||
|
||||
export function startup() {
|
||||
if (typeof toc !== 'undefined') {
|
||||
// toc might be immutable
|
||||
while (toc.length > 0) toc.pop()
|
||||
toc.push(...[{"depth":2,"title":"Buy museum tickets","url":"#buy-museum-tickets"},{"depth":2,"title":"Get ticket QR code","url":"#get-ticket-qr-code"}])
|
||||
}
|
||||
|
||||
if (typeof structuredData !== 'undefined') {
|
||||
structuredData.headings = [{"content":"Buy museum tickets","id":"buy-museum-tickets"},{"content":"Get ticket QR code","id":"get-ticket-qr-code"}]
|
||||
structuredData.contents = [{"content":"Purchase museum tickets for general entry or special events.","heading":"buy-museum-tickets"},{"content":"Return an image of your ticket with scannable QR code. Used for event entry.","heading":"get-ticket-qr-code"}]
|
||||
}
|
||||
}
|
||||
|
||||
{startup()}
|
||||
<APIPage document={"./fixtures/museum.yaml"} operations={[{"method":"post","path":"/tickets"},{"method":"get","path":"/tickets/{ticketId}/qr"}]} hasHead={true} />
|
||||
@@ -23,19 +23,4 @@ _openapi:
|
||||
contents: []
|
||||
---
|
||||
|
||||
<APIPage document={"./fixtures/petstore.yaml"} operations={[{"method":"get","path":"/pets"},{"method":"post","path":"/pets"},{"method":"get","path":"/pets/{petId}"}]} hasHead={true} />
|
||||
|
||||
export function startup() {
|
||||
if (typeof toc !== 'undefined') {
|
||||
// toc might be immutable
|
||||
while (toc.length > 0) toc.pop()
|
||||
toc.push(...[{"depth":2,"title":"List all pets","url":"#list-all-pets"},{"depth":2,"title":"Create a pet","url":"#create-a-pet"},{"depth":2,"title":"Info for a specific pet","url":"#info-for-a-specific-pet"}])
|
||||
}
|
||||
|
||||
if (typeof structuredData !== 'undefined') {
|
||||
structuredData.headings = [{"content":"List all pets","id":"list-all-pets"},{"content":"Create a pet","id":"create-a-pet"},{"content":"Info for a specific pet","id":"info-for-a-specific-pet"}]
|
||||
structuredData.contents = []
|
||||
}
|
||||
}
|
||||
|
||||
{startup()}
|
||||
<APIPage document={"./fixtures/petstore.yaml"} operations={[{"method":"get","path":"/pets"},{"method":"post","path":"/pets"},{"method":"get","path":"/pets/{petId}"}]} hasHead={true} />
|
||||
@@ -28,19 +28,4 @@ _openapi:
|
||||
contents: []
|
||||
---
|
||||
|
||||
<APIPage document={"./fixtures/unkey.json"} operations={[{"method":"get","path":"/v1/apis.getApi"},{"method":"post","path":"/v1/apis.createApi"},{"method":"get","path":"/v1/apis.listKeys"},{"method":"post","path":"/v1/apis.deleteApi"}]} hasHead={true} />
|
||||
|
||||
export function startup() {
|
||||
if (typeof toc !== 'undefined') {
|
||||
// toc might be immutable
|
||||
while (toc.length > 0) toc.pop()
|
||||
toc.push(...[{"depth":2,"title":"Get Api","url":"#get-api"},{"depth":2,"title":"Create Api","url":"#create-api"},{"depth":2,"title":"List Keys","url":"#list-keys"},{"depth":2,"title":"Delete Api","url":"#delete-api"}])
|
||||
}
|
||||
|
||||
if (typeof structuredData !== 'undefined') {
|
||||
structuredData.headings = [{"content":"Get Api","id":"get-api"},{"content":"Create Api","id":"create-api"},{"content":"List Keys","id":"list-keys"},{"content":"Delete Api","id":"delete-api"}]
|
||||
structuredData.contents = []
|
||||
}
|
||||
}
|
||||
|
||||
{startup()}
|
||||
<APIPage document={"./fixtures/unkey.json"} operations={[{"method":"get","path":"/v1/apis.getApi"},{"method":"post","path":"/v1/apis.createApi"},{"method":"get","path":"/v1/apis.listKeys"},{"method":"post","path":"/v1/apis.deleteApi"}]} hasHead={true} />
|
||||
@@ -43,19 +43,4 @@ _openapi:
|
||||
contents: []
|
||||
---
|
||||
|
||||
<APIPage document={"./fixtures/unkey.json"} operations={[{"method":"get","path":"/v1/keys.getKey"},{"method":"post","path":"/v1/keys.deleteKey"},{"method":"post","path":"/v1/keys.createKey"},{"method":"post","path":"/v1/keys.verifyKey"},{"method":"post","path":"/v1/keys.updateKey"},{"method":"post","path":"/v1/keys.updateRemaining"},{"method":"get","path":"/v1/keys.getVerifications"}]} hasHead={true} />
|
||||
|
||||
export function startup() {
|
||||
if (typeof toc !== 'undefined') {
|
||||
// toc might be immutable
|
||||
while (toc.length > 0) toc.pop()
|
||||
toc.push(...[{"depth":2,"title":"Get Key","url":"#get-key"},{"depth":2,"title":"Delete Key","url":"#delete-key"},{"depth":2,"title":"Create Key","url":"#create-key"},{"depth":2,"title":"Verify Key","url":"#verify-key"},{"depth":2,"title":"Update Key","url":"#update-key"},{"depth":2,"title":"Update Remaining","url":"#update-remaining"},{"depth":2,"title":"Get Verifications","url":"#get-verifications"}])
|
||||
}
|
||||
|
||||
if (typeof structuredData !== 'undefined') {
|
||||
structuredData.headings = [{"content":"Get Key","id":"get-key"},{"content":"Delete Key","id":"delete-key"},{"content":"Create Key","id":"create-key"},{"content":"Verify Key","id":"verify-key"},{"content":"Update Key","id":"update-key"},{"content":"Update Remaining","id":"update-remaining"},{"content":"Get Verifications","id":"get-verifications"}]
|
||||
structuredData.contents = []
|
||||
}
|
||||
}
|
||||
|
||||
{startup()}
|
||||
<APIPage document={"./fixtures/unkey.json"} operations={[{"method":"get","path":"/v1/keys.getKey"},{"method":"post","path":"/v1/keys.deleteKey"},{"method":"post","path":"/v1/keys.createKey"},{"method":"post","path":"/v1/keys.verifyKey"},{"method":"post","path":"/v1/keys.updateKey"},{"method":"post","path":"/v1/keys.updateRemaining"},{"method":"get","path":"/v1/keys.getVerifications"}]} hasHead={true} />
|
||||
@@ -13,19 +13,4 @@ _openapi:
|
||||
contents: []
|
||||
---
|
||||
|
||||
<APIPage document={"./fixtures/unkey.json"} operations={[{"method":"get","path":"/v1/liveness"}]} hasHead={true} />
|
||||
|
||||
export function startup() {
|
||||
if (typeof toc !== 'undefined') {
|
||||
// toc might be immutable
|
||||
while (toc.length > 0) toc.pop()
|
||||
toc.push(...[{"depth":2,"title":"Check Liveness","url":"#check-liveness"}])
|
||||
}
|
||||
|
||||
if (typeof structuredData !== 'undefined') {
|
||||
structuredData.headings = [{"content":"Check Liveness","id":"check-liveness"}]
|
||||
structuredData.contents = []
|
||||
}
|
||||
}
|
||||
|
||||
{startup()}
|
||||
<APIPage document={"./fixtures/unkey.json"} operations={[{"method":"get","path":"/v1/liveness"}]} hasHead={true} />
|
||||
Generated
-45
@@ -698,9 +698,6 @@ importers:
|
||||
react:
|
||||
specifier: '>= 18'
|
||||
version: 18.3.1
|
||||
rehype-img-size:
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1
|
||||
ws:
|
||||
specifier: ^8.18.0
|
||||
version: 8.18.0([email protected])
|
||||
@@ -4587,11 +4584,6 @@ packages:
|
||||
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
hasBin: true
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==}
|
||||
engines: {node: '>=16.x'}
|
||||
@@ -5936,10 +5928,6 @@ packages:
|
||||
resolution: {integrity: sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==}
|
||||
hasBin: true
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-+rLkxF2H3mQULAg3iA2Z2spJQlBcCpApG8sHC47bc0p33ol+ddz+O3gyUcTgk5xX5jGaj1oQOBs/cBy8nIIhoQ==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-h8FPkGE00r2XKU+/acgqwWUlyzve1IiOKwsEkg4pDL3k48PiE0Pt+/uLtVHDVkN1yA4iurZN6UES8ivHVEQV6Q==}
|
||||
|
||||
@@ -6648,9 +6636,6 @@ packages:
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
|
||||
|
||||
@@ -6666,15 +6651,9 @@ packages:
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
|
||||
|
||||
@@ -10930,10 +10909,6 @@ snapshots:
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
queue: 6.0.2
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
queue: 6.0.2
|
||||
@@ -12529,11 +12504,6 @@ snapshots:
|
||||
dependencies:
|
||||
jsesc: 0.5.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
image-size: 1.0.2
|
||||
unist-util-visit: 4.1.2
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
@@ -13371,10 +13341,6 @@ snapshots:
|
||||
'@types/unist': 3.0.2
|
||||
unist-util-is: 6.0.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 2.0.8
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.2
|
||||
@@ -13396,22 +13362,11 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.2
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 2.0.8
|
||||
unist-util-is: 5.2.1
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.2
|
||||
unist-util-is: 6.0.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 2.0.8
|
||||
unist-util-is: 5.2.1
|
||||
unist-util-visit-parents: 5.1.3
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.2
|
||||
|
||||
Reference in New Issue
Block a user