fumadocs-openapi 9.3.0 → 9.3.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.
@@ -68,9 +68,16 @@ interface IndexItem {
68
68
  title?: string;
69
69
  description?: string;
70
70
  /**
71
- * Only include items from specific input schema ids
71
+ * Specify linked pages:
72
+ * - items in `inputs` to include all generated pages of a specific schema.
73
+ * - specific Markdown/MDX files.
72
74
  */
73
- only?: string[];
75
+ only?: (string | OutputFile)[];
76
+ }
77
+ interface HookContext {
78
+ files: OutputFile[];
79
+ readonly generated: Record<string, OutputFile[]>;
80
+ readonly documents: Record<string, ProcessedDocument>;
74
81
  }
75
82
  interface BaseConfig extends GenerateOptions {
76
83
  /**
@@ -91,7 +98,7 @@ interface BaseConfig extends GenerateOptions {
91
98
  * Generate index files with cards linking to generated pages.
92
99
  */
93
100
  index?: {
94
- items: IndexItem[];
101
+ items: IndexItem[] | ((ctx: HookContext) => IndexItem[]);
95
102
  /**
96
103
  * Generate URLs for cards
97
104
  */
@@ -106,7 +113,7 @@ interface BaseConfig extends GenerateOptions {
106
113
  /**
107
114
  * Can add/change/remove output files before writing to file system
108
115
  **/
109
- beforeWrite?: (files: OutputFile[]) => void | Promise<void>;
116
+ beforeWrite?: (this: HookContext, files: OutputFile[]) => void | Promise<void>;
110
117
  }
111
118
  export declare function generateFiles(options: Config): Promise<void>;
112
119
  export declare function generateFilesOnly(options: Config): Promise<OutputFile[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"generate-file.d.ts","sourceRoot":"","sources":["../src/generate-file.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,kBAAkB,EAEvB,KAAK,iBAAiB,EAEvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAI9C,UAAU,kBAAkB;IAC1B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,eAAgB,SAAQ,UAAU;IAC1C;;OAEG;IACH,GAAG,CAAC,EAAE,WAAW,CAAC;IAElB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;IAEnC;;OAEG;IACH,IAAI,CAAC,EACD,CAAC,CACC,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,iBAAiB,CAAC,UAAU,CAAC,KACpC,MAAM,CAAC,GACZ,QAAQ,CAAC;CACd;AAED,UAAU,SAAU,SAAQ,UAAU;IACpC;;OAEG;IACH,GAAG,EAAE,KAAK,CAAC;IAEX;;OAEG;IACH,IAAI,CAAC,EACD,CAAC,CACC,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,iBAAiB,CAAC,UAAU,CAAC,KACpC,MAAM,CAAC,GACZ,QAAQ,CAAC;CACd;AAED,UAAU,UAAW,SAAQ,UAAU;IACrC;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,IAAI,CAAC,EACD,CAAC,CACC,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,iBAAiB,CAAC,UAAU,CAAC,KACpC,MAAM,CAAC,GACZ,QAAQ,CAAC;CACd;AAED,MAAM,MAAM,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,eAAe,CAAC;AAE9D,UAAU,QAAQ;IAChB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACzB;AAED,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,UAAU,UAAW,SAAQ,eAAe;IAC1C;;OAEG;IACH,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,aAAa,CAAC;IAEzC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAEnC;;OAEG;IACH,KAAK,CAAC,EAAE;QACN,KAAK,EAAE,SAAS,EAAE,CAAC;QAEnB;;WAEG;QACH,GAAG,EACC,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,GAC9B;YACE,OAAO,EAAE,MAAM,CAAC;YAChB;;eAEG;YACH,UAAU,EAAE,MAAM,CAAC;SACpB,CAAC;KACP,CAAC;IAEF;;QAEI;IACJ,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUlE;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,UAAU,EAAE,CAAC,CA8CvB"}
1
+ {"version":3,"file":"generate-file.d.ts","sourceRoot":"","sources":["../src/generate-file.ts"],"names":[],"mappings":"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"}
@@ -17,40 +17,52 @@ export async function generateFilesOnly(options) {
17
17
  const { cwd = process.cwd(), beforeWrite } = options;
18
18
  const input = typeof options.input === 'string' ? [options.input] : options.input;
19
19
  let schemas = {};
20
- if (Array.isArray(input)) {
21
- const targets = [];
22
- const patterns = [];
23
- for (const item of input) {
24
- if (isUrl(item))
25
- targets.push(item);
26
- else
27
- patterns.push(item);
20
+ async function resolveInput(item) {
21
+ if (isUrl(item)) {
22
+ schemas[item] = await processDocumentCached(item);
23
+ return;
24
+ }
25
+ const resolved = await glob(item, { cwd, absolute: true });
26
+ if (resolved.length > 1) {
27
+ console.warn('glob patterns in `input` are deprecated, please specify your schemas explicitly.');
28
+ for (let i = 0; i < resolved.length; i++) {
29
+ schemas[`${item}[${i}]`] = await processDocumentCached(item);
30
+ }
31
+ }
32
+ else if (resolved.length === 1) {
33
+ schemas[item] = await processDocumentCached(resolved[0]);
34
+ }
35
+ else {
36
+ throw new Error(`input not found: ${item}`);
28
37
  }
29
- if (patterns.length > 0)
30
- targets.push(...(await glob(patterns, { cwd })));
31
- await Promise.all(targets.map(async (item) => {
32
- schemas[item] = await processDocumentCached(path.join(cwd, item));
33
- }));
38
+ }
39
+ if (Array.isArray(input)) {
40
+ await Promise.all(input.map(resolveInput));
34
41
  }
35
42
  else {
36
43
  schemas = await input.getSchemas();
37
44
  }
38
- const resolvedSchemas = Object.entries(schemas);
39
- if (resolvedSchemas.length === 0) {
45
+ const generated = {};
46
+ const files = [];
47
+ const entries = Object.entries(schemas);
48
+ if (entries.length === 0) {
40
49
  throw new Error('No input files found.');
41
50
  }
42
- const documentFiles = new Map();
43
- const files = [];
44
- for (const [id, schema] of resolvedSchemas) {
51
+ for (const [id, schema] of entries) {
45
52
  const result = generateFromDocument(id, schema, options);
46
53
  files.push(...result);
47
- documentFiles.set(id, result);
54
+ generated[id] = result;
48
55
  }
56
+ const context = {
57
+ files,
58
+ generated,
59
+ documents: schemas,
60
+ };
49
61
  if (options.index) {
50
- files.push(...generateIndexFiles(documentFiles, options));
62
+ writeIndexFiles(context, options);
51
63
  }
52
- beforeWrite?.(files);
53
- return files;
64
+ await beforeWrite?.call(context, context.files);
65
+ return context.files;
54
66
  }
55
67
  function generateFromDocument(schemaId, processed, options) {
56
68
  const files = [];
@@ -171,11 +183,10 @@ function getOutputPathFromRoute(path) {
171
183
  })
172
184
  .join('/') ?? '');
173
185
  }
174
- function generateIndexFiles(generatedFiles, options) {
175
- const files = [];
186
+ function writeIndexFiles(context, options) {
176
187
  const { index, output, cwd = process.cwd() } = options;
177
188
  if (!index)
178
- return files;
189
+ return;
179
190
  const { items, url } = index;
180
191
  let urlFn;
181
192
  if (typeof url === 'object') {
@@ -186,25 +197,25 @@ function generateIndexFiles(generatedFiles, options) {
186
197
  else {
187
198
  urlFn = url;
188
199
  }
189
- function fileContent(item) {
200
+ function fileContent(index) {
201
+ const generatedPages = context.generated;
190
202
  const content = [];
191
203
  content.push('<Cards>');
192
- const files = [];
193
- if (item.only) {
194
- for (let id of item.only) {
195
- if (id.startsWith('./'))
196
- id = id.slice(2);
197
- const result = generatedFiles.get(id);
198
- if (!result)
199
- throw new Error(`${id} does not exist on "input", available: ${Array.from(generatedFiles.keys())}.`);
200
- files.push(...result);
204
+ const files = new Map();
205
+ const only = index.only ?? Object.keys(context.generated);
206
+ for (const item of only) {
207
+ if (typeof item === 'object') {
208
+ files.set(item.path, item);
209
+ continue;
210
+ }
211
+ const result = generatedPages[item];
212
+ if (!result)
213
+ throw new Error(`${item} does not exist on "input", available: ${Object.keys(generatedPages).join(', ')}.`);
214
+ for (const file of result) {
215
+ files.set(file.path, file);
201
216
  }
202
217
  }
203
- else {
204
- for (const value of generatedFiles.values())
205
- files.push(...value);
206
- }
207
- for (const file of files) {
218
+ for (const file of files.values()) {
208
219
  const isContent = file.path.endsWith('.mdx') || file.path.endsWith('.md');
209
220
  if (!isContent)
210
221
  continue;
@@ -215,18 +226,18 @@ function generateIndexFiles(generatedFiles, options) {
215
226
  }
216
227
  content.push('</Cards>');
217
228
  return generateDocument({
218
- title: item.title ?? 'Overview',
219
- description: item.description,
229
+ title: index.title ?? 'Overview',
230
+ description: index.description,
220
231
  }, content.join('\n'), options);
221
232
  }
222
233
  const outputDir = path.join(cwd, output);
223
- for (const item of items) {
224
- files.push({
225
- path: path.join(outputDir, path.extname(item.path).length === 0 ? `${item.path}.mdx` : item.path),
234
+ for (const item of typeof items === 'function' ? items(context) : items) {
235
+ const outPath = path.join(outputDir, path.extname(item.path).length === 0 ? `${item.path}.mdx` : item.path);
236
+ context.files.push({
237
+ path: outPath,
226
238
  content: fileContent(item),
227
239
  });
228
240
  }
229
- return files;
230
241
  }
231
242
  function defaultSlugify(s) {
232
243
  return s.replace(/\s+/g, '-').toLowerCase();
@@ -1 +1 @@
1
- {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/utils/url.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM9D;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,MAAM,CAMR;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,WAAW,GAC3B,MAAM,CAyBR"}
1
+ {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/utils/url.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM9D;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,MAAM,CAMR;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,WAAW,GAC3B,MAAM,CAoCR"}
package/dist/utils/url.js CHANGED
@@ -25,6 +25,7 @@ export function resolveServerUrl(template, variables) {
25
25
  return template;
26
26
  }
27
27
  export function resolveRequestData(pathname, { path, query }) {
28
+ // First, resolve path parameters in the pathname
28
29
  for (const key in path) {
29
30
  const param = path[key];
30
31
  if (Array.isArray(param.value)) {
@@ -34,17 +35,25 @@ export function resolveRequestData(pathname, { path, query }) {
34
35
  pathname = pathname.replace(`{${key}}`, param.value);
35
36
  }
36
37
  }
37
- const searchParams = new URLSearchParams();
38
+ // Check if pathname already contains query parameters (legacy API support)
39
+ const [pathPart, existingQueryString] = pathname.split('?', 2);
40
+ // Parse existing query parameters from the pathname if they exist
41
+ const searchParams = new URLSearchParams(existingQueryString || '');
42
+ // Add new query parameters from the RequestData
38
43
  for (const key in query) {
39
44
  const param = query[key];
40
45
  if (Array.isArray(param.value)) {
46
+ // Remove existing parameter first to avoid duplicates
47
+ searchParams.delete(key);
41
48
  for (const item of param.value) {
42
49
  searchParams.append(key, item);
43
50
  }
44
51
  }
45
52
  else {
46
- searchParams.append(key, param.value);
53
+ // Set (replace if exists) the parameter value
54
+ searchParams.set(key, param.value);
47
55
  }
48
56
  }
49
- return searchParams.size > 0 ? `${pathname}?${searchParams}` : pathname;
57
+ // Return the reconstructed URL
58
+ return searchParams.size > 0 ? `${pathPart}?${searchParams}` : pathPart;
50
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-openapi",
3
- "version": "9.3.0",
3
+ "version": "9.3.2",
4
4
  "description": "Generate MDX docs for your OpenAPI spec",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -62,18 +62,18 @@
62
62
  "react-hook-form": "^7.62.0",
63
63
  "remark": "^15.0.1",
64
64
  "remark-rehype": "^11.1.2",
65
- "shiki": "^3.11.0",
65
+ "shiki": "^3.12.0",
66
66
  "tinyglobby": "^0.2.14",
67
67
  "xml-js": "^1.6.11",
68
- "fumadocs-core": "15.7.3",
69
- "fumadocs-ui": "15.7.3"
68
+ "fumadocs-core": "15.7.5",
69
+ "fumadocs-ui": "15.7.5"
70
70
  },
71
71
  "devDependencies": {
72
- "@scalar/api-client-react": "^1.3.29",
72
+ "@scalar/api-client-react": "^1.3.31",
73
73
  "@types/js-yaml": "^4.0.9",
74
74
  "@types/node": "24.3.0",
75
75
  "@types/openapi-sampler": "^1.0.3",
76
- "@types/react": "^19.1.11",
76
+ "@types/react": "^19.1.12",
77
77
  "json-schema-typed": "^8.0.1",
78
78
  "openapi-types": "^12.1.3",
79
79
  "tailwindcss": "^4.1.12",