docusaurus-plugin-openapi-docs 0.0.0-1075 → 0.0.0-1077

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/README.md CHANGED
@@ -328,6 +328,16 @@ yarn docusaurus gen-api-docs all --all-versions
328
328
 
329
329
  > This will generate API docs for all versions of all the OpenAPI specification (OAS) files referenced in your `docusaurus-plugin-openapi-docs` config.
330
330
 
331
+ To generate only schema MDX files—without updating the sidebar or requiring `showSchemas` in your plugin config—use the `--schemas-only` flag:
332
+
333
+ ```bash
334
+ yarn docusaurus gen-api-docs petstore --schemas-only
335
+ ```
336
+
337
+ > This command writes the schema pages to the configured output directory while leaving other generated docs untouched.
338
+
339
+ The `--schemas-only` flag is also available for `gen-api-docs:version`.
340
+
331
341
  ### Cleaning API Docs
332
342
 
333
343
  To clean/remove all API Docs, run the following command from the root directory of your project:
@@ -360,6 +370,14 @@ yarn docusaurus clean-api-docs all --all-versions
360
370
 
361
371
  > This will clean API docs for all versions of all the OpenAPI specification (OAS) files referenced in your `docusaurus-plugin-openapi-docs` config.
362
372
 
373
+ To clean only schema docs while leaving API, info, and tag docs untouched, use the `--schemas-only` flag:
374
+
375
+ ```bash
376
+ yarn docusaurus clean-api-docs petstore --schemas-only
377
+ ```
378
+
379
+ > The `--schemas-only` flag is also available for `clean-api-docs:version`.
380
+
363
381
  ### Versioning OpenAPI docs
364
382
 
365
383
  To generate _all_ versioned OpenAPI docs, run the following command from the root directory of your project:
package/lib/index.js CHANGED
@@ -78,7 +78,8 @@ function pluginOpenAPIDocs(context, options) {
78
78
  let docPath = docData ? (docData.path ? docData.path : "docs") : undefined;
79
79
  async function generateApiDocs(options, pluginId) {
80
80
  var _a, _b, _c, _d;
81
- let { specPath, outputDir, template, infoTemplate, tagTemplate, schemaTemplate, markdownGenerators, downloadUrl, sidebarOptions, disableCompression, } = options;
81
+ let { specPath, outputDir, template, infoTemplate, tagTemplate, schemaTemplate, markdownGenerators, downloadUrl, sidebarOptions, schemasOnly, disableCompression, } = options;
82
+ const isSchemasOnly = schemasOnly === true;
82
83
  // Remove trailing slash before proceeding
83
84
  outputDir = outputDir.replace(/\/$/, "");
84
85
  // Override docPath if pluginId provided
@@ -103,7 +104,7 @@ function pluginOpenAPIDocs(context, options) {
103
104
  }
104
105
  }
105
106
  // TODO: figure out better way to set default
106
- if (Object.keys(sidebarOptions !== null && sidebarOptions !== void 0 ? sidebarOptions : {}).length > 0) {
107
+ if (!isSchemasOnly && Object.keys(sidebarOptions !== null && sidebarOptions !== void 0 ? sidebarOptions : {}).length > 0) {
107
108
  const sidebarSlice = (0, sidebars_1.default)(sidebarOptions, options, loadedApi, tags, docPath, tagGroups);
108
109
  let sidebarSliceTemplate = `import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";\n\n`;
109
110
  sidebarSliceTemplate += `const sidebar: SidebarsConfig = {{{slice}}};\n\n`;
@@ -238,6 +239,9 @@ custom_edit_url: null
238
239
  }
239
240
  const markdown = pageGeneratorByType[item.type](item);
240
241
  item.markdown = markdown;
242
+ if (isSchemasOnly && item.type !== "schema") {
243
+ return;
244
+ }
241
245
  if (item.type === "api") {
242
246
  // opportunity to compress JSON
243
247
  // const serialize = (o: any) => {
@@ -338,25 +342,36 @@ custom_edit_url: null
338
342
  throw e;
339
343
  }
340
344
  }
341
- async function cleanApiDocs(options) {
345
+ async function cleanApiDocs(options, schemasOnly = false) {
342
346
  const { outputDir } = options;
343
347
  const apiDir = (0, utils_1.posixPath)(path_1.default.join(siteDir, outputDir));
344
- const apiMdxFiles = await (0, utils_1.Globby)(["*.api.mdx", "*.info.mdx", "*.tag.mdx"], {
345
- cwd: path_1.default.resolve(apiDir),
346
- deep: 1,
347
- });
348
- const sidebarFile = await (0, utils_1.Globby)(["sidebar.js", "sidebar.ts"], {
349
- cwd: path_1.default.resolve(apiDir),
350
- deep: 1,
351
- });
352
- apiMdxFiles.map((mdx) => fs_1.default.unlink(`${apiDir}/${mdx}`, (err) => {
353
- if (err) {
354
- console.error(chalk_1.default.red(`Cleanup failed for "${apiDir}/${mdx}"`), chalk_1.default.yellow(err));
355
- }
356
- else {
357
- console.log(chalk_1.default.green(`Cleanup succeeded for "${apiDir}/${mdx}"`));
358
- }
359
- }));
348
+ // When schemasOnly is true, only clean the schemas directory
349
+ if (!schemasOnly) {
350
+ const apiMdxFiles = await (0, utils_1.Globby)(["*.api.mdx", "*.info.mdx", "*.tag.mdx"], {
351
+ cwd: path_1.default.resolve(apiDir),
352
+ deep: 1,
353
+ });
354
+ const sidebarFile = await (0, utils_1.Globby)(["sidebar.js", "sidebar.ts"], {
355
+ cwd: path_1.default.resolve(apiDir),
356
+ deep: 1,
357
+ });
358
+ apiMdxFiles.map((mdx) => fs_1.default.unlink(`${apiDir}/${mdx}`, (err) => {
359
+ if (err) {
360
+ console.error(chalk_1.default.red(`Cleanup failed for "${apiDir}/${mdx}"`), chalk_1.default.yellow(err));
361
+ }
362
+ else {
363
+ console.log(chalk_1.default.green(`Cleanup succeeded for "${apiDir}/${mdx}"`));
364
+ }
365
+ }));
366
+ sidebarFile.map((sidebar) => fs_1.default.unlink(`${apiDir}/${sidebar}`, (err) => {
367
+ if (err) {
368
+ console.error(chalk_1.default.red(`Cleanup failed for "${apiDir}/${sidebar}"`), chalk_1.default.yellow(err));
369
+ }
370
+ else {
371
+ console.log(chalk_1.default.green(`Cleanup succeeded for "${apiDir}/${sidebar}"`));
372
+ }
373
+ }));
374
+ }
360
375
  try {
361
376
  fs_1.default.rmSync(`${apiDir}/schemas`, { recursive: true });
362
377
  console.log(chalk_1.default.green(`Cleanup succeeded for "${apiDir}/schemas"`));
@@ -366,14 +381,6 @@ custom_edit_url: null
366
381
  console.error(chalk_1.default.red(`Cleanup failed for "${apiDir}/schemas"`), chalk_1.default.yellow(err));
367
382
  }
368
383
  }
369
- sidebarFile.map((sidebar) => fs_1.default.unlink(`${apiDir}/${sidebar}`, (err) => {
370
- if (err) {
371
- console.error(chalk_1.default.red(`Cleanup failed for "${apiDir}/${sidebar}"`), chalk_1.default.yellow(err));
372
- }
373
- else {
374
- console.log(chalk_1.default.green(`Cleanup succeeded for "${apiDir}/${sidebar}"`));
375
- }
376
- }));
377
384
  }
378
385
  async function generateVersions(versions, outputDir) {
379
386
  let versionsArray = [];
@@ -445,19 +452,21 @@ custom_edit_url: null
445
452
  });
446
453
  }
447
454
  }
448
- async function cleanAllVersions(options) {
455
+ async function cleanAllVersions(options, schemasOnly = false) {
449
456
  const parentOptions = Object.assign({}, options);
450
457
  const { versions } = parentOptions;
451
458
  delete parentOptions.versions;
452
459
  if (versions != null && Object.keys(versions).length > 0) {
453
- await cleanVersions(parentOptions.outputDir);
460
+ if (!schemasOnly) {
461
+ await cleanVersions(parentOptions.outputDir);
462
+ }
454
463
  Object.keys(versions).forEach(async (key) => {
455
464
  const versionOptions = versions[key];
456
465
  const mergedOptions = {
457
466
  ...parentOptions,
458
467
  ...versionOptions,
459
468
  };
460
- await cleanApiDocs(mergedOptions);
469
+ await cleanApiDocs(mergedOptions, schemasOnly);
461
470
  });
462
471
  }
463
472
  }
@@ -471,11 +480,13 @@ custom_edit_url: null
471
480
  .arguments("<id>")
472
481
  .option("-p, --plugin-id <plugin>", "OpenAPI docs plugin ID.")
473
482
  .option("--all-versions", "Generate all versions.")
483
+ .option("--schemas-only", "Generate only schema docs.")
474
484
  .action(async (id, instance) => {
475
485
  var _a;
476
486
  const options = instance.opts();
477
487
  const pluginId = options.pluginId;
478
488
  const allVersions = options.allVersions;
489
+ const schemasOnly = options.schemasOnly;
479
490
  const pluginInstances = getPluginInstances(plugins);
480
491
  let targetConfig;
481
492
  let targetDocsPluginId;
@@ -497,15 +508,17 @@ custom_edit_url: null
497
508
  }
498
509
  targetConfig = config;
499
510
  }
511
+ const withSchemaOverride = (apiOptions) => schemasOnly ? { ...apiOptions, schemasOnly: true } : apiOptions;
500
512
  if (id === "all") {
501
513
  if (targetConfig[id]) {
502
514
  console.error(chalk_1.default.red("Can't use id 'all' for OpenAPI docs configuration key."));
503
515
  }
504
516
  else {
505
517
  Object.keys(targetConfig).forEach(async function (key) {
506
- await generateApiDocs(targetConfig[key], targetDocsPluginId);
518
+ const apiOptions = withSchemaOverride(targetConfig[key]);
519
+ await generateApiDocs(apiOptions, targetDocsPluginId);
507
520
  if (allVersions) {
508
- await generateAllVersions(targetConfig[key], targetDocsPluginId);
521
+ await generateAllVersions(apiOptions, targetDocsPluginId);
509
522
  }
510
523
  });
511
524
  }
@@ -514,9 +527,10 @@ custom_edit_url: null
514
527
  console.error(chalk_1.default.red(`ID '${id}' does not exist in OpenAPI docs config.`));
515
528
  }
516
529
  else {
517
- await generateApiDocs(targetConfig[id], targetDocsPluginId);
530
+ const apiOptions = withSchemaOverride(targetConfig[id]);
531
+ await generateApiDocs(apiOptions, targetDocsPluginId);
518
532
  if (allVersions) {
519
- await generateAllVersions(targetConfig[id], targetDocsPluginId);
533
+ await generateAllVersions(apiOptions, targetDocsPluginId);
520
534
  }
521
535
  }
522
536
  });
@@ -526,10 +540,12 @@ custom_edit_url: null
526
540
  .usage("<id:version>")
527
541
  .arguments("<id:version>")
528
542
  .option("-p, --plugin-id <plugin>", "OpenAPI docs plugin ID.")
543
+ .option("--schemas-only", "Generate only schema docs.")
529
544
  .action(async (id, instance) => {
530
545
  var _a;
531
546
  const options = instance.opts();
532
547
  const pluginId = options.pluginId;
548
+ const schemasOnly = options.schemasOnly;
533
549
  const pluginInstances = getPluginInstances(plugins);
534
550
  let targetConfig;
535
551
  let targetDocsPluginId;
@@ -553,6 +569,7 @@ custom_edit_url: null
553
569
  }
554
570
  const [parentId, versionId] = id.split(":");
555
571
  const parentConfig = Object.assign({}, targetConfig[parentId]);
572
+ const withSchemaOverride = (apiOptions) => schemasOnly ? { ...apiOptions, schemasOnly: true } : apiOptions;
556
573
  const version = parentConfig.version;
557
574
  const label = parentConfig.label;
558
575
  const baseUrl = parentConfig.baseUrl;
@@ -575,10 +592,10 @@ custom_edit_url: null
575
592
  await generateVersions(mergedVersions, parentConfig.outputDir);
576
593
  Object.keys(versions).forEach(async (key) => {
577
594
  const versionConfig = versions[key];
578
- const mergedConfig = {
595
+ const mergedConfig = withSchemaOverride({
579
596
  ...parentConfig,
580
597
  ...versionConfig,
581
- };
598
+ });
582
599
  await generateApiDocs(mergedConfig, targetDocsPluginId);
583
600
  });
584
601
  }
@@ -588,10 +605,10 @@ custom_edit_url: null
588
605
  }
589
606
  else {
590
607
  const versionConfig = versions[versionId];
591
- const mergedConfig = {
608
+ const mergedConfig = withSchemaOverride({
592
609
  ...parentConfig,
593
610
  ...versionConfig,
594
- };
611
+ });
595
612
  await generateVersions(mergedVersions, parentConfig.outputDir);
596
613
  await generateApiDocs(mergedConfig, targetDocsPluginId);
597
614
  }
@@ -603,11 +620,13 @@ custom_edit_url: null
603
620
  .arguments("<id>")
604
621
  .option("-p, --plugin-id <plugin>", "OpenAPI docs plugin ID.")
605
622
  .option("--all-versions", "Clean all versions.")
623
+ .option("--schemas-only", "Clean only schema docs.")
606
624
  .action(async (id, instance) => {
607
625
  var _a;
608
626
  const options = instance.opts();
609
627
  const pluginId = options.pluginId;
610
628
  const allVersions = options.allVersions;
629
+ const schemasOnly = options.schemasOnly;
611
630
  const pluginInstances = getPluginInstances(plugins);
612
631
  let targetConfig;
613
632
  if (pluginId) {
@@ -633,17 +652,17 @@ custom_edit_url: null
633
652
  }
634
653
  else {
635
654
  Object.keys(targetConfig).forEach(async function (key) {
636
- await cleanApiDocs(targetConfig[key]);
655
+ await cleanApiDocs(targetConfig[key], schemasOnly);
637
656
  if (allVersions) {
638
- await cleanAllVersions(targetConfig[key]);
657
+ await cleanAllVersions(targetConfig[key], schemasOnly);
639
658
  }
640
659
  });
641
660
  }
642
661
  }
643
662
  else {
644
- await cleanApiDocs(targetConfig[id]);
663
+ await cleanApiDocs(targetConfig[id], schemasOnly);
645
664
  if (allVersions) {
646
- await cleanAllVersions(targetConfig[id]);
665
+ await cleanAllVersions(targetConfig[id], schemasOnly);
647
666
  }
648
667
  }
649
668
  });
@@ -653,10 +672,12 @@ custom_edit_url: null
653
672
  .usage("<id:version>")
654
673
  .arguments("<id:version>")
655
674
  .option("-p, --plugin-id <plugin>", "OpenAPI docs plugin ID.")
675
+ .option("--schemas-only", "Clean only schema docs.")
656
676
  .action(async (id, instance) => {
657
677
  var _a;
658
678
  const options = instance.opts();
659
679
  const pluginId = options.pluginId;
680
+ const schemasOnly = options.schemasOnly;
660
681
  const pluginInstances = getPluginInstances(plugins);
661
682
  let targetConfig;
662
683
  if (pluginId) {
@@ -685,14 +706,16 @@ custom_edit_url: null
685
706
  chalk_1.default.red("Can't use id 'all' for OpenAPI docs versions configuration key.");
686
707
  }
687
708
  else {
688
- await cleanVersions(parentConfig.outputDir);
709
+ if (!schemasOnly) {
710
+ await cleanVersions(parentConfig.outputDir);
711
+ }
689
712
  Object.keys(versions).forEach(async (key) => {
690
713
  const versionConfig = versions[key];
691
714
  const mergedConfig = {
692
715
  ...parentConfig,
693
716
  ...versionConfig,
694
717
  };
695
- await cleanApiDocs(mergedConfig);
718
+ await cleanApiDocs(mergedConfig, schemasOnly);
696
719
  });
697
720
  }
698
721
  }
@@ -702,7 +725,7 @@ custom_edit_url: null
702
725
  ...parentConfig,
703
726
  ...versionConfig,
704
727
  };
705
- await cleanApiDocs(mergedConfig);
728
+ await cleanApiDocs(mergedConfig, schemasOnly);
706
729
  }
707
730
  });
708
731
  },
@@ -108,6 +108,7 @@ function createItems(openapiData, options, sidebarOptions) {
108
108
  let items = [];
109
109
  const infoIdSpaces = openapiData.info.title.replace(" ", "-").toLowerCase();
110
110
  const infoId = (0, kebabCase_1.default)(infoIdSpaces);
111
+ const schemasOnly = (options === null || options === void 0 ? void 0 : options.schemasOnly) === true;
111
112
  if (openapiData.info.description || openapiData.info.title) {
112
113
  // Only create an info page if we have a description.
113
114
  const infoDescription = (_a = openapiData.info) === null || _a === void 0 ? void 0 : _a.description;
@@ -378,13 +379,16 @@ function createItems(openapiData, options, sidebarOptions) {
378
379
  items.push(apiPage);
379
380
  }
380
381
  }
381
- if ((options === null || options === void 0 ? void 0 : options.showSchemas) === true ||
382
+ if (schemasOnly ||
383
+ (options === null || options === void 0 ? void 0 : options.showSchemas) === true ||
382
384
  Object.entries((_3 = (_2 = openapiData === null || openapiData === void 0 ? void 0 : openapiData.components) === null || _2 === void 0 ? void 0 : _2.schemas) !== null && _3 !== void 0 ? _3 : {})
383
385
  .flatMap(([_, s]) => s["x-tags"])
384
386
  .filter((item) => !!item).length > 0) {
385
387
  // Gather schemas
386
388
  for (let [schema, schemaObject] of Object.entries((_5 = (_4 = openapiData === null || openapiData === void 0 ? void 0 : openapiData.components) === null || _4 === void 0 ? void 0 : _4.schemas) !== null && _5 !== void 0 ? _5 : {})) {
387
- if ((options === null || options === void 0 ? void 0 : options.showSchemas) === true || schemaObject["x-tags"]) {
389
+ if (schemasOnly ||
390
+ (options === null || options === void 0 ? void 0 : options.showSchemas) === true ||
391
+ schemaObject["x-tags"]) {
388
392
  const baseIdSpaces = (_7 = (_6 = schemaObject === null || schemaObject === void 0 ? void 0 : schemaObject.title) === null || _6 === void 0 ? void 0 : _6.replace(" ", "-").toLowerCase()) !== null && _7 !== void 0 ? _7 : "";
389
393
  const baseId = (0, kebabCase_1.default)(baseIdSpaces);
390
394
  const schemaDescription = schemaObject.description;
@@ -13,6 +13,7 @@ const path_1 = __importDefault(require("path"));
13
13
  // eslint-disable-next-line import/no-extraneous-dependencies
14
14
  const utils_1 = require("@docusaurus/utils");
15
15
  const _1 = require(".");
16
+ const openapi_1 = require("./openapi");
16
17
  // npx jest packages/docusaurus-plugin-openapi/src/openapi/openapi.test.ts --watch
17
18
  describe("openapi", () => {
18
19
  describe("readOpenapiFiles", () => {
@@ -30,4 +31,51 @@ describe("openapi", () => {
30
31
  expect((_b = (_a = yaml === null || yaml === void 0 ? void 0 : yaml.data.components) === null || _a === void 0 ? void 0 : _a.schemas) === null || _b === void 0 ? void 0 : _b.HelloString["x-tags"]).toBeDefined();
31
32
  });
32
33
  });
34
+ describe("schemasOnly", () => {
35
+ it("includes schema metadata when showSchemas is disabled", async () => {
36
+ const openapiData = {
37
+ openapi: "3.0.0",
38
+ info: {
39
+ title: "Schema Only",
40
+ version: "1.0.0",
41
+ },
42
+ paths: {
43
+ "/ping": {
44
+ get: {
45
+ summary: "Ping",
46
+ responses: {
47
+ "200": {
48
+ description: "OK",
49
+ },
50
+ },
51
+ },
52
+ },
53
+ },
54
+ components: {
55
+ schemas: {
56
+ WithoutTags: {
57
+ title: "Without Tags",
58
+ type: "object",
59
+ properties: {
60
+ value: {
61
+ type: "string",
62
+ },
63
+ },
64
+ },
65
+ },
66
+ },
67
+ };
68
+ const options = {
69
+ specPath: "dummy", // required by the type but unused in this context
70
+ outputDir: "build",
71
+ showSchemas: false,
72
+ schemasOnly: true,
73
+ };
74
+ const sidebarOptions = {};
75
+ const [items] = await (0, openapi_1.processOpenapiFile)(openapiData, options, sidebarOptions);
76
+ const schemaItems = items.filter((item) => item.type === "schema");
77
+ expect(schemaItems).toHaveLength(1);
78
+ expect(schemaItems[0].id).toBe("without-tags");
79
+ });
80
+ });
33
81
  });
package/lib/types.d.ts CHANGED
@@ -29,6 +29,7 @@ export interface APIOptions {
29
29
  proxy?: string;
30
30
  markdownGenerators?: MarkdownGenerator;
31
31
  showSchemas?: boolean;
32
+ schemasOnly?: boolean;
32
33
  disableCompression?: boolean;
33
34
  maskCredentials?: boolean;
34
35
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "docusaurus-plugin-openapi-docs",
3
3
  "description": "OpenAPI plugin for Docusaurus.",
4
- "version": "0.0.0-1075",
4
+ "version": "0.0.0-1077",
5
5
  "license": "MIT",
6
6
  "keywords": [
7
7
  "openapi",
@@ -65,5 +65,5 @@
65
65
  "engines": {
66
66
  "node": ">=14"
67
67
  },
68
- "gitHead": "d4ec26e753c5bab96246a39a07b4ccd5a5ae8344"
68
+ "gitHead": "9a7f0853967fa5b5e0241a48f65f36e1f7d3d591"
69
69
  }
package/src/index.ts CHANGED
@@ -123,9 +123,12 @@ export default function pluginOpenAPIDocs(
123
123
  markdownGenerators,
124
124
  downloadUrl,
125
125
  sidebarOptions,
126
+ schemasOnly,
126
127
  disableCompression,
127
128
  } = options;
128
129
 
130
+ const isSchemasOnly = schemasOnly === true;
131
+
129
132
  // Remove trailing slash before proceeding
130
133
  outputDir = outputDir.replace(/\/$/, "");
131
134
 
@@ -160,7 +163,7 @@ export default function pluginOpenAPIDocs(
160
163
  }
161
164
 
162
165
  // TODO: figure out better way to set default
163
- if (Object.keys(sidebarOptions ?? {}).length > 0) {
166
+ if (!isSchemasOnly && Object.keys(sidebarOptions ?? {}).length > 0) {
164
167
  const sidebarSlice = generateSidebarSlice(
165
168
  sidebarOptions!,
166
169
  options,
@@ -332,6 +335,9 @@ custom_edit_url: null
332
335
  }
333
336
  const markdown = pageGeneratorByType[item.type](item as any);
334
337
  item.markdown = markdown;
338
+ if (isSchemasOnly && item.type !== "schema") {
339
+ return;
340
+ }
335
341
  if (item.type === "api") {
336
342
  // opportunity to compress JSON
337
343
  // const serialize = (o: any) => {
@@ -485,29 +491,53 @@ custom_edit_url: null
485
491
  }
486
492
  }
487
493
 
488
- async function cleanApiDocs(options: APIOptions) {
494
+ async function cleanApiDocs(options: APIOptions, schemasOnly = false) {
489
495
  const { outputDir } = options;
490
496
  const apiDir = posixPath(path.join(siteDir, outputDir));
491
- const apiMdxFiles = await Globby(["*.api.mdx", "*.info.mdx", "*.tag.mdx"], {
492
- cwd: path.resolve(apiDir),
493
- deep: 1,
494
- });
495
- const sidebarFile = await Globby(["sidebar.js", "sidebar.ts"], {
496
- cwd: path.resolve(apiDir),
497
- deep: 1,
498
- });
499
- apiMdxFiles.map((mdx) =>
500
- fs.unlink(`${apiDir}/${mdx}`, (err) => {
501
- if (err) {
502
- console.error(
503
- chalk.red(`Cleanup failed for "${apiDir}/${mdx}"`),
504
- chalk.yellow(err)
505
- );
506
- } else {
507
- console.log(chalk.green(`Cleanup succeeded for "${apiDir}/${mdx}"`));
497
+
498
+ // When schemasOnly is true, only clean the schemas directory
499
+ if (!schemasOnly) {
500
+ const apiMdxFiles = await Globby(
501
+ ["*.api.mdx", "*.info.mdx", "*.tag.mdx"],
502
+ {
503
+ cwd: path.resolve(apiDir),
504
+ deep: 1,
508
505
  }
509
- })
510
- );
506
+ );
507
+ const sidebarFile = await Globby(["sidebar.js", "sidebar.ts"], {
508
+ cwd: path.resolve(apiDir),
509
+ deep: 1,
510
+ });
511
+ apiMdxFiles.map((mdx) =>
512
+ fs.unlink(`${apiDir}/${mdx}`, (err) => {
513
+ if (err) {
514
+ console.error(
515
+ chalk.red(`Cleanup failed for "${apiDir}/${mdx}"`),
516
+ chalk.yellow(err)
517
+ );
518
+ } else {
519
+ console.log(
520
+ chalk.green(`Cleanup succeeded for "${apiDir}/${mdx}"`)
521
+ );
522
+ }
523
+ })
524
+ );
525
+
526
+ sidebarFile.map((sidebar) =>
527
+ fs.unlink(`${apiDir}/${sidebar}`, (err) => {
528
+ if (err) {
529
+ console.error(
530
+ chalk.red(`Cleanup failed for "${apiDir}/${sidebar}"`),
531
+ chalk.yellow(err)
532
+ );
533
+ } else {
534
+ console.log(
535
+ chalk.green(`Cleanup succeeded for "${apiDir}/${sidebar}"`)
536
+ );
537
+ }
538
+ })
539
+ );
540
+ }
511
541
 
512
542
  try {
513
543
  fs.rmSync(`${apiDir}/schemas`, { recursive: true });
@@ -520,21 +550,6 @@ custom_edit_url: null
520
550
  );
521
551
  }
522
552
  }
523
-
524
- sidebarFile.map((sidebar) =>
525
- fs.unlink(`${apiDir}/${sidebar}`, (err) => {
526
- if (err) {
527
- console.error(
528
- chalk.red(`Cleanup failed for "${apiDir}/${sidebar}"`),
529
- chalk.yellow(err)
530
- );
531
- } else {
532
- console.log(
533
- chalk.green(`Cleanup succeeded for "${apiDir}/${sidebar}"`)
534
- );
535
- }
536
- })
537
- );
538
553
  }
539
554
 
540
555
  async function generateVersions(versions: object, outputDir: string) {
@@ -635,7 +650,7 @@ custom_edit_url: null
635
650
  }
636
651
  }
637
652
 
638
- async function cleanAllVersions(options: APIOptions) {
653
+ async function cleanAllVersions(options: APIOptions, schemasOnly = false) {
639
654
  const parentOptions = Object.assign({}, options);
640
655
 
641
656
  const { versions } = parentOptions as any;
@@ -643,14 +658,16 @@ custom_edit_url: null
643
658
  delete parentOptions.versions;
644
659
 
645
660
  if (versions != null && Object.keys(versions).length > 0) {
646
- await cleanVersions(parentOptions.outputDir);
661
+ if (!schemasOnly) {
662
+ await cleanVersions(parentOptions.outputDir);
663
+ }
647
664
  Object.keys(versions).forEach(async (key) => {
648
665
  const versionOptions = versions[key];
649
666
  const mergedOptions = {
650
667
  ...parentOptions,
651
668
  ...versionOptions,
652
669
  };
653
- await cleanApiDocs(mergedOptions);
670
+ await cleanApiDocs(mergedOptions, schemasOnly);
654
671
  });
655
672
  }
656
673
  }
@@ -668,10 +685,12 @@ custom_edit_url: null
668
685
  .arguments("<id>")
669
686
  .option("-p, --plugin-id <plugin>", "OpenAPI docs plugin ID.")
670
687
  .option("--all-versions", "Generate all versions.")
688
+ .option("--schemas-only", "Generate only schema docs.")
671
689
  .action(async (id, instance) => {
672
690
  const options = instance.opts();
673
691
  const pluginId = options.pluginId;
674
692
  const allVersions = options.allVersions;
693
+ const schemasOnly = options.schemasOnly;
675
694
  const pluginInstances = getPluginInstances(plugins);
676
695
  let targetConfig: any;
677
696
  let targetDocsPluginId: any;
@@ -698,6 +717,9 @@ custom_edit_url: null
698
717
  targetConfig = config;
699
718
  }
700
719
 
720
+ const withSchemaOverride = (apiOptions: APIOptions): APIOptions =>
721
+ schemasOnly ? { ...apiOptions, schemasOnly: true } : apiOptions;
722
+
701
723
  if (id === "all") {
702
724
  if (targetConfig[id]) {
703
725
  console.error(
@@ -707,12 +729,10 @@ custom_edit_url: null
707
729
  );
708
730
  } else {
709
731
  Object.keys(targetConfig).forEach(async function (key) {
710
- await generateApiDocs(targetConfig[key], targetDocsPluginId);
732
+ const apiOptions = withSchemaOverride(targetConfig[key]);
733
+ await generateApiDocs(apiOptions, targetDocsPluginId);
711
734
  if (allVersions) {
712
- await generateAllVersions(
713
- targetConfig[key],
714
- targetDocsPluginId
715
- );
735
+ await generateAllVersions(apiOptions, targetDocsPluginId);
716
736
  }
717
737
  });
718
738
  }
@@ -721,9 +741,10 @@ custom_edit_url: null
721
741
  chalk.red(`ID '${id}' does not exist in OpenAPI docs config.`)
722
742
  );
723
743
  } else {
724
- await generateApiDocs(targetConfig[id], targetDocsPluginId);
744
+ const apiOptions = withSchemaOverride(targetConfig[id]);
745
+ await generateApiDocs(apiOptions, targetDocsPluginId);
725
746
  if (allVersions) {
726
- await generateAllVersions(targetConfig[id], targetDocsPluginId);
747
+ await generateAllVersions(apiOptions, targetDocsPluginId);
727
748
  }
728
749
  }
729
750
  });
@@ -736,9 +757,11 @@ custom_edit_url: null
736
757
  .usage("<id:version>")
737
758
  .arguments("<id:version>")
738
759
  .option("-p, --plugin-id <plugin>", "OpenAPI docs plugin ID.")
760
+ .option("--schemas-only", "Generate only schema docs.")
739
761
  .action(async (id, instance) => {
740
762
  const options = instance.opts();
741
763
  const pluginId = options.pluginId;
764
+ const schemasOnly = options.schemasOnly;
742
765
  const pluginInstances = getPluginInstances(plugins);
743
766
  let targetConfig: any;
744
767
  let targetDocsPluginId: any;
@@ -767,6 +790,9 @@ custom_edit_url: null
767
790
  const [parentId, versionId] = id.split(":");
768
791
  const parentConfig = Object.assign({}, targetConfig[parentId]);
769
792
 
793
+ const withSchemaOverride = (apiOptions: APIOptions): APIOptions =>
794
+ schemasOnly ? { ...apiOptions, schemasOnly: true } : apiOptions;
795
+
770
796
  const version = parentConfig.version as string;
771
797
  const label = parentConfig.label as string;
772
798
  const baseUrl = parentConfig.baseUrl as string;
@@ -796,10 +822,10 @@ custom_edit_url: null
796
822
  await generateVersions(mergedVersions, parentConfig.outputDir);
797
823
  Object.keys(versions).forEach(async (key) => {
798
824
  const versionConfig = versions[key];
799
- const mergedConfig = {
825
+ const mergedConfig = withSchemaOverride({
800
826
  ...parentConfig,
801
827
  ...versionConfig,
802
- };
828
+ });
803
829
  await generateApiDocs(mergedConfig, targetDocsPluginId);
804
830
  });
805
831
  }
@@ -811,10 +837,10 @@ custom_edit_url: null
811
837
  );
812
838
  } else {
813
839
  const versionConfig = versions[versionId];
814
- const mergedConfig = {
840
+ const mergedConfig = withSchemaOverride({
815
841
  ...parentConfig,
816
842
  ...versionConfig,
817
- };
843
+ });
818
844
  await generateVersions(mergedVersions, parentConfig.outputDir);
819
845
  await generateApiDocs(mergedConfig, targetDocsPluginId);
820
846
  }
@@ -829,10 +855,12 @@ custom_edit_url: null
829
855
  .arguments("<id>")
830
856
  .option("-p, --plugin-id <plugin>", "OpenAPI docs plugin ID.")
831
857
  .option("--all-versions", "Clean all versions.")
858
+ .option("--schemas-only", "Clean only schema docs.")
832
859
  .action(async (id, instance) => {
833
860
  const options = instance.opts();
834
861
  const pluginId = options.pluginId;
835
862
  const allVersions = options.allVersions;
863
+ const schemasOnly = options.schemasOnly;
836
864
  const pluginInstances = getPluginInstances(plugins);
837
865
  let targetConfig: any;
838
866
  if (pluginId) {
@@ -865,16 +893,16 @@ custom_edit_url: null
865
893
  );
866
894
  } else {
867
895
  Object.keys(targetConfig).forEach(async function (key) {
868
- await cleanApiDocs(targetConfig[key]);
896
+ await cleanApiDocs(targetConfig[key], schemasOnly);
869
897
  if (allVersions) {
870
- await cleanAllVersions(targetConfig[key]);
898
+ await cleanAllVersions(targetConfig[key], schemasOnly);
871
899
  }
872
900
  });
873
901
  }
874
902
  } else {
875
- await cleanApiDocs(targetConfig[id]);
903
+ await cleanApiDocs(targetConfig[id], schemasOnly);
876
904
  if (allVersions) {
877
- await cleanAllVersions(targetConfig[id]);
905
+ await cleanAllVersions(targetConfig[id], schemasOnly);
878
906
  }
879
907
  }
880
908
  });
@@ -887,9 +915,11 @@ custom_edit_url: null
887
915
  .usage("<id:version>")
888
916
  .arguments("<id:version>")
889
917
  .option("-p, --plugin-id <plugin>", "OpenAPI docs plugin ID.")
918
+ .option("--schemas-only", "Clean only schema docs.")
890
919
  .action(async (id, instance) => {
891
920
  const options = instance.opts();
892
921
  const pluginId = options.pluginId;
922
+ const schemasOnly = options.schemasOnly;
893
923
  const pluginInstances = getPluginInstances(plugins);
894
924
  let targetConfig: any;
895
925
  if (pluginId) {
@@ -925,14 +955,16 @@ custom_edit_url: null
925
955
  "Can't use id 'all' for OpenAPI docs versions configuration key."
926
956
  );
927
957
  } else {
928
- await cleanVersions(parentConfig.outputDir);
958
+ if (!schemasOnly) {
959
+ await cleanVersions(parentConfig.outputDir);
960
+ }
929
961
  Object.keys(versions).forEach(async (key) => {
930
962
  const versionConfig = versions[key];
931
963
  const mergedConfig = {
932
964
  ...parentConfig,
933
965
  ...versionConfig,
934
966
  };
935
- await cleanApiDocs(mergedConfig);
967
+ await cleanApiDocs(mergedConfig, schemasOnly);
936
968
  });
937
969
  }
938
970
  } else {
@@ -941,7 +973,7 @@ custom_edit_url: null
941
973
  ...parentConfig,
942
974
  ...versionConfig,
943
975
  };
944
- await cleanApiDocs(mergedConfig);
976
+ await cleanApiDocs(mergedConfig, schemasOnly);
945
977
  }
946
978
  });
947
979
  },
@@ -11,6 +11,8 @@ import path from "path";
11
11
  import { posixPath } from "@docusaurus/utils";
12
12
 
13
13
  import { readOpenapiFiles } from ".";
14
+ import { processOpenapiFile } from "./openapi";
15
+ import type { APIOptions, SidebarOptions } from "../types";
14
16
 
15
17
  // npx jest packages/docusaurus-plugin-openapi/src/openapi/openapi.test.ts --watch
16
18
 
@@ -37,4 +39,60 @@ describe("openapi", () => {
37
39
  ).toBeDefined();
38
40
  });
39
41
  });
42
+
43
+ describe("schemasOnly", () => {
44
+ it("includes schema metadata when showSchemas is disabled", async () => {
45
+ const openapiData = {
46
+ openapi: "3.0.0",
47
+ info: {
48
+ title: "Schema Only",
49
+ version: "1.0.0",
50
+ },
51
+ paths: {
52
+ "/ping": {
53
+ get: {
54
+ summary: "Ping",
55
+ responses: {
56
+ "200": {
57
+ description: "OK",
58
+ },
59
+ },
60
+ },
61
+ },
62
+ },
63
+ components: {
64
+ schemas: {
65
+ WithoutTags: {
66
+ title: "Without Tags",
67
+ type: "object",
68
+ properties: {
69
+ value: {
70
+ type: "string",
71
+ },
72
+ },
73
+ },
74
+ },
75
+ },
76
+ };
77
+
78
+ const options: APIOptions = {
79
+ specPath: "dummy", // required by the type but unused in this context
80
+ outputDir: "build",
81
+ showSchemas: false,
82
+ schemasOnly: true,
83
+ };
84
+
85
+ const sidebarOptions = {} as SidebarOptions;
86
+
87
+ const [items] = await processOpenapiFile(
88
+ openapiData as any,
89
+ options,
90
+ sidebarOptions
91
+ );
92
+
93
+ const schemaItems = items.filter((item) => item.type === "schema");
94
+ expect(schemaItems).toHaveLength(1);
95
+ expect(schemaItems[0].id).toBe("without-tags");
96
+ });
97
+ });
40
98
  });
@@ -95,6 +95,7 @@ function createItems(
95
95
  let items: PartialPage<ApiMetadata>[] = [];
96
96
  const infoIdSpaces = openapiData.info.title.replace(" ", "-").toLowerCase();
97
97
  const infoId = kebabCase(infoIdSpaces);
98
+ const schemasOnly = options?.schemasOnly === true;
98
99
 
99
100
  if (openapiData.info.description || openapiData.info.title) {
100
101
  // Only create an info page if we have a description.
@@ -434,6 +435,7 @@ function createItems(
434
435
  }
435
436
 
436
437
  if (
438
+ schemasOnly ||
437
439
  options?.showSchemas === true ||
438
440
  Object.entries(openapiData?.components?.schemas ?? {})
439
441
  .flatMap(([_, s]) => s["x-tags"])
@@ -443,7 +445,11 @@ function createItems(
443
445
  for (let [schema, schemaObject] of Object.entries(
444
446
  openapiData?.components?.schemas ?? {}
445
447
  )) {
446
- if (options?.showSchemas === true || schemaObject["x-tags"]) {
448
+ if (
449
+ schemasOnly ||
450
+ options?.showSchemas === true ||
451
+ schemaObject["x-tags"]
452
+ ) {
447
453
  const baseIdSpaces =
448
454
  schemaObject?.title?.replace(" ", "-").toLowerCase() ?? "";
449
455
  const baseId = kebabCase(baseIdSpaces);
package/src/types.ts CHANGED
@@ -51,6 +51,7 @@ export interface APIOptions {
51
51
  proxy?: string;
52
52
  markdownGenerators?: MarkdownGenerator;
53
53
  showSchemas?: boolean;
54
+ schemasOnly?: boolean;
54
55
  disableCompression?: boolean;
55
56
  maskCredentials?: boolean;
56
57
  }