mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
OpenAPI: fix typescript schema generation
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'fumadocs-openapi': patch
|
||||
---
|
||||
|
||||
Fix TypeScript schema wrong output.
|
||||
|
||||
Note: code formatting has been disabled to improve performance.
|
||||
@@ -52,10 +52,6 @@ When set to `custom`, you can pass a function to fully customise the generation
|
||||
import { generateFiles, OperationOutput } from 'fumadocs-openapi';
|
||||
|
||||
void generateFiles({
|
||||
// ---cut-start---
|
||||
input: {} as any,
|
||||
output: {} as any,
|
||||
// ---cut-end---
|
||||
per: 'custom',
|
||||
toPages(builder) {
|
||||
const { dereferenced } = builder.document;
|
||||
@@ -82,6 +78,15 @@ void generateFiles({
|
||||
});
|
||||
```
|
||||
|
||||
When set to `tag`, adding `x-displayName` to the tag definition can control the title of each page.
|
||||
|
||||
```yaml title="openapi.yaml"
|
||||
tags:
|
||||
- name: test
|
||||
description: this is a tag.
|
||||
x-displayName: My Test Name
|
||||
```
|
||||
|
||||
### `groupBy`
|
||||
|
||||
In `operation` mode, you can group output files with folders.
|
||||
@@ -217,14 +222,3 @@ void generateFiles({
|
||||
addGeneratedComment: false,
|
||||
});
|
||||
```
|
||||
|
||||
### Tag Display Name
|
||||
|
||||
Adding `x-displayName` to OpenAPI Schema can control the display name of your tags.
|
||||
|
||||
```yaml title="openapi.yaml"
|
||||
tags:
|
||||
- name: test
|
||||
description: this is a tag.
|
||||
x-displayName: My Test Name
|
||||
```
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"@ai-sdk/openai-compatible": "^1.0.26",
|
||||
"@ai-sdk/react": "^2.0.87",
|
||||
"@fumadocs/mdx-remote": "workspace:*",
|
||||
"@mixedbread/sdk": "^0.37.0",
|
||||
"@mixedbread/sdk": "^0.38.0",
|
||||
"@orama/core": "^1.2.13",
|
||||
"@orama/orama": "^3.1.16",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
@@ -88,7 +88,7 @@
|
||||
"shadcn": "3.5.0",
|
||||
"tailwindcss": "^4.1.16",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tinyexec": "^1.0.1",
|
||||
"tinyexec": "^1.0.2",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"ts-morph": "^27.0.2",
|
||||
"tsconfig": "workspace:*",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"react-router": "^7.9.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@remix-run/node-fetch-server": "^0.11.0",
|
||||
"@remix-run/node-fetch-server": "^0.12.0",
|
||||
"@tailwindcss/vite": "^4.1.16",
|
||||
"@types/mdx": "^2.0.13",
|
||||
"@types/node": "^24.10.0",
|
||||
@@ -28,6 +28,6 @@
|
||||
"react-router-devtools": "^5.1.3",
|
||||
"tailwindcss": "^4.1.16",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.1.12"
|
||||
"vite": "^7.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"react-router-devtools": "^5.1.3",
|
||||
"tailwindcss": "^4.1.16",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.1.12",
|
||||
"vite": "^7.2.0",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"react-router-devtools": "^5.1.3",
|
||||
"tailwindcss": "^4.1.16",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.1.12",
|
||||
"vite": "^7.2.0",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"serve": "^14.2.5",
|
||||
"tailwindcss": "^4.1.16",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.1.12",
|
||||
"vite": "^7.2.0",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"react-router-devtools": "^5.1.3",
|
||||
"tailwindcss": "^4.1.16",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.1.12",
|
||||
"vite": "^7.2.0",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"vite": "^7.1.12"
|
||||
"vite": "^7.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.1.16",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"vite": "^7.1.12"
|
||||
"vite": "^7.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.1.16",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"vite": "^7.1.12"
|
||||
"vite": "^7.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.1.16",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@types/react-dom": "^19.2.2",
|
||||
"tailwindcss": "^4.1.16",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.1.12",
|
||||
"vite": "^7.2.0",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"@types/react-dom": "^19.2.2",
|
||||
"tailwindcss": "^4.1.16",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.1.12",
|
||||
"vite": "^7.2.0",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@
|
||||
"turbo": "2.6.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.46.3",
|
||||
"vitest": "^4.0.6"
|
||||
"vitest": "^4.0.7"
|
||||
},
|
||||
"packageManager": "[email protected]",
|
||||
"engines": {
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"commander": "^14.0.2",
|
||||
"package-manager-detector": "^1.5.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"tinyexec": "^1.0.1",
|
||||
"tinyexec": "^1.0.2",
|
||||
"ts-morph": "^27.0.2",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mdx-js/mdx": "^3.1.1",
|
||||
"@mixedbread/sdk": "^0.37.0",
|
||||
"@mixedbread/sdk": "^0.38.0",
|
||||
"@orama/core": "^1.2.13",
|
||||
"@tanstack/react-router": "^1.134.12",
|
||||
"@types/estree-jsx": "^1.0.5",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"@commander-js/extra-typings": "^14.0.0",
|
||||
"commander": "^14.0.2",
|
||||
"picocolors": "^1.1.1",
|
||||
"tinyexec": "^1.0.1",
|
||||
"tinyexec": "^1.0.2",
|
||||
"ts-morph": "^27.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
"picocolors": "^1.1.1",
|
||||
"picomatch": "^4.0.3",
|
||||
"remark-mdx": "^3.1.1",
|
||||
"tinyexec": "^1.0.1",
|
||||
"tinyexec": "^1.0.2",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"unified": "^11.0.5",
|
||||
"unist-util-remove-position": "^5.0.0",
|
||||
@@ -123,7 +123,7 @@
|
||||
"rollup": "^4.52.5",
|
||||
"tsconfig": "workspace:*",
|
||||
"vfile": "^6.0.3",
|
||||
"vite": "^7.1.12",
|
||||
"vite": "^7.2.0",
|
||||
"webpack": "^5.102.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -56,13 +56,13 @@
|
||||
"types:check": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fumari/json-schema-to-typescript": "^1.1.3",
|
||||
"@fumari/json-schema-to-typescript": "^2.0.0",
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@scalar/json-magic": "^0.6.1",
|
||||
"@scalar/openapi-parser": "0.22.3",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@scalar/json-magic": "^0.7.0",
|
||||
"@scalar/openapi-parser": "0.23.0",
|
||||
"ajv": "^8.17.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"fumadocs-core": "workspace:*",
|
||||
@@ -80,7 +80,7 @@
|
||||
"xml-js": "^1.6.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scalar/api-client-react": "^1.3.46",
|
||||
"@scalar/api-client-react": "^1.3.48",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "24.10.0",
|
||||
"@types/openapi-sampler": "^1.0.3",
|
||||
|
||||
@@ -336,7 +336,7 @@ async function ResponseAccordion({
|
||||
if (generateTypeScriptSchema) {
|
||||
ts = await generateTypeScriptSchema(operation, status);
|
||||
} else if (generateTypeScriptSchema === undefined && schema) {
|
||||
ts = await getTypescriptSchema(ctx.schema);
|
||||
ts = await getTypescriptSchema(schema, ctx);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
import type { ProcessedDocument } from '@/utils/process-document';
|
||||
import type { SchemaObject } from 'ajv';
|
||||
import type { RenderContext } from '@/types';
|
||||
|
||||
export async function getTypescriptSchema(
|
||||
processed: ProcessedDocument,
|
||||
schema: SchemaObject,
|
||||
ctx: RenderContext,
|
||||
): Promise<string | undefined> {
|
||||
try {
|
||||
const { compile } = await import('@fumari/json-schema-to-typescript');
|
||||
const { compile } = await import('@fumari/json-schema-to-typescript');
|
||||
|
||||
const cloned = structuredClone(processed.bundled);
|
||||
return await compile(cloned, 'Response', {
|
||||
$refOptions: false,
|
||||
bannerComment: '',
|
||||
additionalProperties: false,
|
||||
try {
|
||||
const input = structuredClone({
|
||||
schema,
|
||||
idToSchema: ctx.schema._internal_idToSchema(),
|
||||
});
|
||||
|
||||
const schemaToId = new WeakMap<object, string>();
|
||||
|
||||
for (const [k, v] of input.idToSchema) {
|
||||
schemaToId.set(v, k);
|
||||
}
|
||||
|
||||
return await compile(input.schema, 'Response', {
|
||||
enableConstEnums: false,
|
||||
schemaToId,
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('Failed to generate typescript schema:', e);
|
||||
|
||||
@@ -11,6 +11,8 @@ export type ProcessedDocument = {
|
||||
*/
|
||||
dereferenced: NoReference<Document>;
|
||||
|
||||
_internal_idToSchema: () => Map<string, object>;
|
||||
|
||||
/**
|
||||
* Get raw object from dereference object
|
||||
*/
|
||||
@@ -61,19 +63,23 @@ export async function processDocument(
|
||||
* Dereferenced value and its original `$ref` value
|
||||
*/
|
||||
const dereferenceMap = new WeakMap<object, string>();
|
||||
const serializable = new Map<string, object>();
|
||||
|
||||
return {
|
||||
dereferenced: (
|
||||
await dereference(document, {
|
||||
throwOnError: true,
|
||||
onDereference({ schema, ref }) {
|
||||
dereferenceMap.set(schema, ref);
|
||||
},
|
||||
})
|
||||
).schema as NoReference<Document>,
|
||||
dereferenced: dereference(document, {
|
||||
throwOnError: true,
|
||||
onDereference({ schema, ref }) {
|
||||
serializable.set(ref, schema);
|
||||
dereferenceMap.set(schema, ref);
|
||||
},
|
||||
}).schema as NoReference<Document>,
|
||||
getRawRef(obj) {
|
||||
return dereferenceMap.get(obj);
|
||||
},
|
||||
|
||||
_internal_idToSchema() {
|
||||
return serializable;
|
||||
},
|
||||
bundled: document as Document,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"picocolors": "^1.1.1",
|
||||
"react-router": "^7.9.5",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"vite": "^7.1.12",
|
||||
"vite": "^7.2.0",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@radix-ui/react-presence": "^1.1.5",
|
||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"fumadocs-core": "workspace:*",
|
||||
|
||||
Generated
+321
-285
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user