docusaurus-plugin-openapi-docs 0.0.0-1092 → 0.0.0-1093

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/lib/index.js CHANGED
@@ -20,7 +20,7 @@ const chalk_1 = __importDefault(require("chalk"));
20
20
  const json5_1 = __importDefault(require("json5"));
21
21
  const mustache_1 = require("mustache");
22
22
  const markdown_1 = require("./markdown");
23
- const externalizeJsonProps_1 = require("./markdown/externalizeJsonProps");
23
+ const utils_2 = require("./markdown/utils");
24
24
  const openapi_1 = require("./openapi");
25
25
  const options_1 = require("./options");
26
26
  const sidebars_1 = __importDefault(require("./sidebars"));
@@ -239,8 +239,17 @@ custom_edit_url: null
239
239
  if (downloadUrl) {
240
240
  item.downloadUrl = downloadUrl;
241
241
  }
242
- const markdown = pageGeneratorByType[item.type](item);
243
- item.markdown = markdown;
242
+ // Generate markdown, with externalization for API and schema pages
243
+ let externalFiles = [];
244
+ if (options.externalJsonProps &&
245
+ (item.type === "api" || item.type === "schema")) {
246
+ const result = (0, utils_2.runWithExternalization)(item.id, () => pageGeneratorByType[item.type](item));
247
+ item.markdown = result.result;
248
+ externalFiles = result.files;
249
+ }
250
+ else {
251
+ item.markdown = pageGeneratorByType[item.type](item);
252
+ }
244
253
  if (isSchemasOnly && item.type !== "schema") {
245
254
  return;
246
255
  }
@@ -282,23 +291,15 @@ custom_edit_url: null
282
291
  if (item.id.length === 0) {
283
292
  throw Error("Operation must have summary or operationId defined");
284
293
  }
285
- let finalView = view;
286
- let jsonFilesToWrite = [];
287
- // Externalize large JSON props if enabled
288
- if (options.externalJsonProps) {
289
- const result = (0, externalizeJsonProps_1.externalizeJsonPropsSimple)(view, item.id);
290
- finalView = result.mdx;
291
- jsonFilesToWrite = result.jsonFiles;
292
- // Write JSON files
293
- for (const jsonFile of jsonFilesToWrite) {
294
- const jsonPath = `${outputDir}/${jsonFile.filename}`;
295
- if (!fs_1.default.existsSync(jsonPath)) {
296
- fs_1.default.writeFileSync(jsonPath, jsonFile.content, "utf8");
297
- console.log(chalk_1.default.green(`Successfully created "${jsonPath}"`));
298
- }
294
+ // Write externalized JSON files
295
+ for (const jsonFile of externalFiles) {
296
+ const jsonPath = `${outputDir}/${jsonFile.filename}`;
297
+ if (!fs_1.default.existsSync(jsonPath)) {
298
+ fs_1.default.writeFileSync(jsonPath, jsonFile.content, "utf8");
299
+ console.log(chalk_1.default.green(`Successfully created "${jsonPath}"`));
299
300
  }
300
301
  }
301
- fs_1.default.writeFileSync(`${outputDir}/${item.id}.api.mdx`, finalView, "utf8");
302
+ fs_1.default.writeFileSync(`${outputDir}/${item.id}.api.mdx`, view, "utf8");
302
303
  console.log(chalk_1.default.green(`Successfully created "${outputDir}/${item.id}.api.mdx"`));
303
304
  }
304
305
  catch (err) {
@@ -347,20 +348,13 @@ custom_edit_url: null
347
348
  throw Error("Schema must have title defined");
348
349
  }
349
350
  // eslint-disable-next-line testing-library/render-result-naming-convention
350
- let schemaView = (0, mustache_1.render)(schemaMdTemplate, item);
351
- let jsonFilesToWrite = [];
352
- // Externalize large JSON props if enabled
353
- if (options.externalJsonProps) {
354
- const result = (0, externalizeJsonProps_1.externalizeJsonPropsSimple)(schemaView, item.id);
355
- schemaView = result.mdx;
356
- jsonFilesToWrite = result.jsonFiles;
357
- // Write JSON files in schemas directory
358
- for (const jsonFile of jsonFilesToWrite) {
359
- const jsonPath = `${outputDir}/schemas/${jsonFile.filename}`;
360
- if (!fs_1.default.existsSync(jsonPath)) {
361
- fs_1.default.writeFileSync(jsonPath, jsonFile.content, "utf8");
362
- console.log(chalk_1.default.green(`Successfully created "${jsonPath}"`));
363
- }
351
+ const schemaView = (0, mustache_1.render)(schemaMdTemplate, item);
352
+ // Write externalized JSON files in schemas directory
353
+ for (const jsonFile of externalFiles) {
354
+ const jsonPath = `${outputDir}/schemas/${jsonFile.filename}`;
355
+ if (!fs_1.default.existsSync(jsonPath)) {
356
+ fs_1.default.writeFileSync(jsonPath, jsonFile.content, "utf8");
357
+ console.log(chalk_1.default.green(`Successfully created "${jsonPath}"`));
364
358
  }
365
359
  }
366
360
  fs_1.default.writeFileSync(`${outputDir}/schemas/${item.id}.schema.mdx`, schemaView, "utf8");
@@ -1,6 +1,38 @@
1
+ /**
2
+ * Represents an external JSON file to be written alongside the MDX.
3
+ */
4
+ export interface ExternalFile {
5
+ /** The filename for the JSON file (relative to outputDir) */
6
+ filename: string;
7
+ /** The JSON content to write */
8
+ content: string;
9
+ }
10
+ /**
11
+ * Result of running MDX generation with externalization.
12
+ */
13
+ export interface ExternalizationResult<T> {
14
+ /** The result of the generation function */
15
+ result: T;
16
+ /** External JSON files to write */
17
+ files: ExternalFile[];
18
+ }
19
+ /**
20
+ * Runs a function with externalization enabled.
21
+ * Any calls to create() within the function will externalize eligible component props.
22
+ *
23
+ * @param baseFilename - Base filename for the MDX file (without extension)
24
+ * @param fn - Function to run with externalization enabled
25
+ * @returns The function result and any external files that were collected
26
+ *
27
+ * @example
28
+ * const { result: mdx, files } = runWithExternalization("add-pet", () => {
29
+ * return createApiPageMD(item);
30
+ * });
31
+ */
32
+ export declare function runWithExternalization<T>(baseFilename: string, fn: () => T): ExternalizationResult<T>;
1
33
  /**
2
34
  * Children in the plugin does not accept DOM elements, when compared with Children in the theme.
3
- * It is designed for rendering HTML a strings.
35
+ * It is designed for rendering HTML as strings.
4
36
  */
5
37
  export type Children = string | undefined | (string | string[] | undefined)[];
6
38
  export type Props = Record<string, any> & {
@@ -9,6 +41,11 @@ export type Props = Record<string, any> & {
9
41
  export type Options = {
10
42
  inline?: boolean;
11
43
  };
44
+ /**
45
+ * Creates a JSX component string with the given tag, props, and options.
46
+ * When called within runWithExternalization(), props for eligible
47
+ * components are externalized to a single JSON file and spread.
48
+ */
12
49
  export declare function create(tag: string, props: Props, options?: Options): string;
13
50
  export declare function guard<T>(value: T | undefined, cb: (value: T) => Children): string;
14
51
  export declare function render(children: Children): string;
@@ -7,15 +7,82 @@
7
7
  * ========================================================================== */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.codeBlock = exports.curlyBrackets = exports.codeFence = exports.greaterThan = exports.lessThan = void 0;
10
+ exports.runWithExternalization = runWithExternalization;
10
11
  exports.create = create;
11
12
  exports.guard = guard;
12
13
  exports.render = render;
13
14
  exports.clean = clean;
15
+ /**
16
+ * Module-level externalization context.
17
+ * Note: AsyncLocalStorage would be cleaner but isn't available in browser bundles.
18
+ */
19
+ let externalizationContext = null;
20
+ /**
21
+ * Components whose props should be externalized to separate JSON files.
22
+ * These are the components that typically receive large JSON objects.
23
+ */
24
+ const EXTERNALIZABLE_COMPONENTS = new Set([
25
+ "StatusCodes",
26
+ "ParamsDetails",
27
+ "RequestSchema",
28
+ "Schema",
29
+ "SchemaItem",
30
+ ]);
31
+ /**
32
+ * Runs a function with externalization enabled.
33
+ * Any calls to create() within the function will externalize eligible component props.
34
+ *
35
+ * @param baseFilename - Base filename for the MDX file (without extension)
36
+ * @param fn - Function to run with externalization enabled
37
+ * @returns The function result and any external files that were collected
38
+ *
39
+ * @example
40
+ * const { result: mdx, files } = runWithExternalization("add-pet", () => {
41
+ * return createApiPageMD(item);
42
+ * });
43
+ */
44
+ function runWithExternalization(baseFilename, fn) {
45
+ // Set up context
46
+ externalizationContext = {
47
+ baseFilename,
48
+ componentCounters: {},
49
+ files: [],
50
+ };
51
+ try {
52
+ const result = fn();
53
+ const files = externalizationContext.files;
54
+ return { result, files };
55
+ }
56
+ finally {
57
+ // Always clear context
58
+ externalizationContext = null;
59
+ }
60
+ }
61
+ /**
62
+ * Creates a JSX component string with the given tag, props, and options.
63
+ * When called within runWithExternalization(), props for eligible
64
+ * components are externalized to a single JSON file and spread.
65
+ */
14
66
  function create(tag, props, options = {}) {
15
67
  const { children, ...rest } = props;
16
68
  let propString = "";
17
- for (const [key, value] of Object.entries(rest)) {
18
- propString += `\n ${key}={${JSON.stringify(value)}}`;
69
+ // Check if this component's props should be externalized
70
+ if (shouldExternalizeComponent(tag, rest)) {
71
+ const filename = generateExternalFilename(tag);
72
+ const content = JSON.stringify(rest);
73
+ // Add to external files
74
+ externalizationContext.files.push({
75
+ filename,
76
+ content,
77
+ });
78
+ // Use spread syntax with require
79
+ propString = `\n {...require("./${filename}")}`;
80
+ }
81
+ else {
82
+ // Inline props as usual
83
+ for (const [key, value] of Object.entries(rest)) {
84
+ propString += `\n ${key}={${JSON.stringify(value)}}`;
85
+ }
19
86
  }
20
87
  let indentedChildren = render(children).replace(/^/gm, " ");
21
88
  if (options.inline) {
@@ -26,6 +93,37 @@ function create(tag, props, options = {}) {
26
93
  indentedChildren += indentedChildren ? "\n" : "";
27
94
  return `<${tag}${propString}>\n${indentedChildren}</${tag}>`;
28
95
  }
96
+ /**
97
+ * Determines if a component's props should be externalized.
98
+ */
99
+ function shouldExternalizeComponent(tag, props) {
100
+ // No context means externalization is not enabled
101
+ if (!externalizationContext) {
102
+ return false;
103
+ }
104
+ if (!EXTERNALIZABLE_COMPONENTS.has(tag)) {
105
+ return false;
106
+ }
107
+ // Don't externalize if props are empty or only contain undefined/null
108
+ const hasContent = Object.values(props).some((v) => v !== undefined && v !== null);
109
+ if (!hasContent) {
110
+ return false;
111
+ }
112
+ return true;
113
+ }
114
+ /**
115
+ * Generates a unique filename for an externalized component's props.
116
+ */
117
+ function generateExternalFilename(componentName) {
118
+ var _a;
119
+ if (!externalizationContext) {
120
+ throw new Error("Externalization context not set");
121
+ }
122
+ const count = ((_a = externalizationContext.componentCounters[componentName]) !== null && _a !== void 0 ? _a : 0) + 1;
123
+ externalizationContext.componentCounters[componentName] = count;
124
+ const suffix = count > 1 ? `.${count}` : "";
125
+ return `${externalizationContext.baseFilename}.${componentName}${suffix}.json`;
126
+ }
29
127
  function guard(value, cb) {
30
128
  if (!!value || value === 0) {
31
129
  const children = cb(value);
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-1092",
4
+ "version": "0.0.0-1093",
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": "52285283e2fc0ee02b7f5e42a9678a3bb89c095b"
68
+ "gitHead": "25ec109b85febce944f00689f6985a89886280ae"
69
69
  }
package/src/index.ts CHANGED
@@ -21,10 +21,7 @@ import {
21
21
  createSchemaPageMD,
22
22
  createTagPageMD,
23
23
  } from "./markdown";
24
- import {
25
- externalizeJsonPropsSimple,
26
- ExternalizedJsonFile,
27
- } from "./markdown/externalizeJsonProps";
24
+ import { ExternalFile, runWithExternalization } from "./markdown/utils";
28
25
  import { processOpenapiFiles, readOpenapiFiles } from "./openapi";
29
26
  import { OptionsSchema } from "./options";
30
27
  import generateSidebarSlice from "./sidebars";
@@ -337,8 +334,21 @@ custom_edit_url: null
337
334
  if (downloadUrl) {
338
335
  item.downloadUrl = downloadUrl;
339
336
  }
340
- const markdown = pageGeneratorByType[item.type](item as any);
341
- item.markdown = markdown;
337
+
338
+ // Generate markdown, with externalization for API and schema pages
339
+ let externalFiles: ExternalFile[] = [];
340
+ if (
341
+ options.externalJsonProps &&
342
+ (item.type === "api" || item.type === "schema")
343
+ ) {
344
+ const result = runWithExternalization(item.id, () =>
345
+ pageGeneratorByType[item.type](item as any)
346
+ );
347
+ item.markdown = result.result;
348
+ externalFiles = result.files;
349
+ } else {
350
+ item.markdown = pageGeneratorByType[item.type](item as any);
351
+ }
342
352
  if (isSchemasOnly && item.type !== "schema") {
343
353
  return;
344
354
  }
@@ -388,32 +398,18 @@ custom_edit_url: null
388
398
  );
389
399
  }
390
400
 
391
- let finalView = view;
392
- let jsonFilesToWrite: ExternalizedJsonFile[] = [];
393
-
394
- // Externalize large JSON props if enabled
395
- if (options.externalJsonProps) {
396
- const result = externalizeJsonPropsSimple(view, item.id);
397
- finalView = result.mdx;
398
- jsonFilesToWrite = result.jsonFiles;
399
-
400
- // Write JSON files
401
- for (const jsonFile of jsonFilesToWrite) {
402
- const jsonPath = `${outputDir}/${jsonFile.filename}`;
403
- if (!fs.existsSync(jsonPath)) {
404
- fs.writeFileSync(jsonPath, jsonFile.content, "utf8");
405
- console.log(
406
- chalk.green(`Successfully created "${jsonPath}"`)
407
- );
408
- }
401
+ // Write externalized JSON files
402
+ for (const jsonFile of externalFiles) {
403
+ const jsonPath = `${outputDir}/${jsonFile.filename}`;
404
+ if (!fs.existsSync(jsonPath)) {
405
+ fs.writeFileSync(jsonPath, jsonFile.content, "utf8");
406
+ console.log(
407
+ chalk.green(`Successfully created "${jsonPath}"`)
408
+ );
409
409
  }
410
410
  }
411
411
 
412
- fs.writeFileSync(
413
- `${outputDir}/${item.id}.api.mdx`,
414
- finalView,
415
- "utf8"
416
- );
412
+ fs.writeFileSync(`${outputDir}/${item.id}.api.mdx`, view, "utf8");
417
413
  console.log(
418
414
  chalk.green(
419
415
  `Successfully created "${outputDir}/${item.id}.api.mdx"`
@@ -499,24 +495,16 @@ custom_edit_url: null
499
495
  throw Error("Schema must have title defined");
500
496
  }
501
497
  // eslint-disable-next-line testing-library/render-result-naming-convention
502
- let schemaView = render(schemaMdTemplate, item);
503
- let jsonFilesToWrite: ExternalizedJsonFile[] = [];
504
-
505
- // Externalize large JSON props if enabled
506
- if (options.externalJsonProps) {
507
- const result = externalizeJsonPropsSimple(schemaView, item.id);
508
- schemaView = result.mdx;
509
- jsonFilesToWrite = result.jsonFiles;
510
-
511
- // Write JSON files in schemas directory
512
- for (const jsonFile of jsonFilesToWrite) {
513
- const jsonPath = `${outputDir}/schemas/${jsonFile.filename}`;
514
- if (!fs.existsSync(jsonPath)) {
515
- fs.writeFileSync(jsonPath, jsonFile.content, "utf8");
516
- console.log(
517
- chalk.green(`Successfully created "${jsonPath}"`)
518
- );
519
- }
498
+ const schemaView = render(schemaMdTemplate, item);
499
+
500
+ // Write externalized JSON files in schemas directory
501
+ for (const jsonFile of externalFiles) {
502
+ const jsonPath = `${outputDir}/schemas/${jsonFile.filename}`;
503
+ if (!fs.existsSync(jsonPath)) {
504
+ fs.writeFileSync(jsonPath, jsonFile.content, "utf8");
505
+ console.log(
506
+ chalk.green(`Successfully created "${jsonPath}"`)
507
+ );
520
508
  }
521
509
  }
522
510
 
@@ -5,9 +5,93 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  * ========================================================================== */
7
7
 
8
+ /**
9
+ * Represents an external JSON file to be written alongside the MDX.
10
+ */
11
+ export interface ExternalFile {
12
+ /** The filename for the JSON file (relative to outputDir) */
13
+ filename: string;
14
+ /** The JSON content to write */
15
+ content: string;
16
+ }
17
+
18
+ /**
19
+ * Result of running MDX generation with externalization.
20
+ */
21
+ export interface ExternalizationResult<T> {
22
+ /** The result of the generation function */
23
+ result: T;
24
+ /** External JSON files to write */
25
+ files: ExternalFile[];
26
+ }
27
+
28
+ /**
29
+ * Context for externalization during MDX generation.
30
+ */
31
+ interface ExternalizationContext {
32
+ /** Base filename for external files (e.g., "add-pet" for "add-pet.api.mdx") */
33
+ baseFilename: string;
34
+ /** Counter for generating unique filenames per component type */
35
+ componentCounters: Record<string, number>;
36
+ /** Collected external files during generation */
37
+ files: ExternalFile[];
38
+ }
39
+
40
+ /**
41
+ * Module-level externalization context.
42
+ * Note: AsyncLocalStorage would be cleaner but isn't available in browser bundles.
43
+ */
44
+ let externalizationContext: ExternalizationContext | null = null;
45
+
46
+ /**
47
+ * Components whose props should be externalized to separate JSON files.
48
+ * These are the components that typically receive large JSON objects.
49
+ */
50
+ const EXTERNALIZABLE_COMPONENTS = new Set([
51
+ "StatusCodes",
52
+ "ParamsDetails",
53
+ "RequestSchema",
54
+ "Schema",
55
+ "SchemaItem",
56
+ ]);
57
+
58
+ /**
59
+ * Runs a function with externalization enabled.
60
+ * Any calls to create() within the function will externalize eligible component props.
61
+ *
62
+ * @param baseFilename - Base filename for the MDX file (without extension)
63
+ * @param fn - Function to run with externalization enabled
64
+ * @returns The function result and any external files that were collected
65
+ *
66
+ * @example
67
+ * const { result: mdx, files } = runWithExternalization("add-pet", () => {
68
+ * return createApiPageMD(item);
69
+ * });
70
+ */
71
+ export function runWithExternalization<T>(
72
+ baseFilename: string,
73
+ fn: () => T
74
+ ): ExternalizationResult<T> {
75
+ // Set up context
76
+ externalizationContext = {
77
+ baseFilename,
78
+ componentCounters: {},
79
+ files: [],
80
+ };
81
+
82
+ try {
83
+ const result = fn();
84
+ const files = externalizationContext.files;
85
+ return { result, files };
86
+ } finally {
87
+ // Always clear context
88
+ externalizationContext = null;
89
+ }
90
+ }
91
+
8
92
  /**
9
93
  * Children in the plugin does not accept DOM elements, when compared with Children in the theme.
10
- * It is designed for rendering HTML a strings.
94
+ * It is designed for rendering HTML as strings.
11
95
  */
12
96
  export type Children = string | undefined | (string | string[] | undefined)[];
13
97
 
@@ -15,6 +99,11 @@ export type Props = Record<string, any> & { children?: Children };
15
99
 
16
100
  export type Options = { inline?: boolean };
17
101
 
102
+ /**
103
+ * Creates a JSX component string with the given tag, props, and options.
104
+ * When called within runWithExternalization(), props for eligible
105
+ * components are externalized to a single JSON file and spread.
106
+ */
18
107
  export function create(
19
108
  tag: string,
20
109
  props: Props,
@@ -23,9 +112,27 @@ export function create(
23
112
  const { children, ...rest } = props;
24
113
 
25
114
  let propString = "";
26
- for (const [key, value] of Object.entries(rest)) {
27
- propString += `\n ${key}={${JSON.stringify(value)}}`;
115
+
116
+ // Check if this component's props should be externalized
117
+ if (shouldExternalizeComponent(tag, rest)) {
118
+ const filename = generateExternalFilename(tag);
119
+ const content = JSON.stringify(rest);
120
+
121
+ // Add to external files
122
+ externalizationContext!.files.push({
123
+ filename,
124
+ content,
125
+ });
126
+
127
+ // Use spread syntax with require
128
+ propString = `\n {...require("./${filename}")}`;
129
+ } else {
130
+ // Inline props as usual
131
+ for (const [key, value] of Object.entries(rest)) {
132
+ propString += `\n ${key}={${JSON.stringify(value)}}`;
133
+ }
28
134
  }
135
+
29
136
  let indentedChildren = render(children).replace(/^/gm, " ");
30
137
 
31
138
  if (options.inline) {
@@ -38,6 +145,49 @@ export function create(
38
145
  return `<${tag}${propString}>\n${indentedChildren}</${tag}>`;
39
146
  }
40
147
 
148
+ /**
149
+ * Determines if a component's props should be externalized.
150
+ */
151
+ function shouldExternalizeComponent(
152
+ tag: string,
153
+ props: Record<string, any>
154
+ ): boolean {
155
+ // No context means externalization is not enabled
156
+ if (!externalizationContext) {
157
+ return false;
158
+ }
159
+
160
+ if (!EXTERNALIZABLE_COMPONENTS.has(tag)) {
161
+ return false;
162
+ }
163
+
164
+ // Don't externalize if props are empty or only contain undefined/null
165
+ const hasContent = Object.values(props).some(
166
+ (v) => v !== undefined && v !== null
167
+ );
168
+ if (!hasContent) {
169
+ return false;
170
+ }
171
+
172
+ return true;
173
+ }
174
+
175
+ /**
176
+ * Generates a unique filename for an externalized component's props.
177
+ */
178
+ function generateExternalFilename(componentName: string): string {
179
+ if (!externalizationContext) {
180
+ throw new Error("Externalization context not set");
181
+ }
182
+
183
+ const count =
184
+ (externalizationContext.componentCounters[componentName] ?? 0) + 1;
185
+ externalizationContext.componentCounters[componentName] = count;
186
+
187
+ const suffix = count > 1 ? `.${count}` : "";
188
+ return `${externalizationContext.baseFilename}.${componentName}${suffix}.json`;
189
+ }
190
+
41
191
  export function guard<T>(
42
192
  value: T | undefined,
43
193
  cb: (value: T) => Children
@@ -1,31 +0,0 @@
1
- /**
2
- * This module provides utilities for externalizing large JSON props in MDX files.
3
- *
4
- * The motivation is to improve MDX compilation performance. When large JSON objects
5
- * are embedded directly as JSX props, MDX needs to parse them into AST and then
6
- * serialize them back. By externalizing to JSON files and using require(), the
7
- * MDX compiler can skip this expensive processing.
8
- *
9
- * @see https://github.com/facebook/docusaurus/discussions/11664
10
- */
11
- export interface ExternalizedJsonFile {
12
- /** The filename for the JSON file (without path) */
13
- filename: string;
14
- /** The JSON content to write */
15
- content: string;
16
- }
17
- export interface ExternalizeResult {
18
- /** The transformed MDX content with require() statements */
19
- mdx: string;
20
- /** List of JSON files that need to be written */
21
- jsonFiles: ExternalizedJsonFile[];
22
- }
23
- /**
24
- * Externalizes large JSON props from MDX content.
25
- * Uses brace-counting to correctly handle deeply nested JSON.
26
- *
27
- * @param mdx - The original MDX content
28
- * @param baseFilename - The base filename (without extension) for the MDX file
29
- * @returns The transformed MDX and list of JSON files to write
30
- */
31
- export declare function externalizeJsonPropsSimple(mdx: string, baseFilename: string): ExternalizeResult;
@@ -1,149 +0,0 @@
1
- "use strict";
2
- /* ============================================================================
3
- * Copyright (c) Palo Alto Networks
4
- *
5
- * This source code is licensed under the MIT license found in the
6
- * LICENSE file in the root directory of this source tree.
7
- * ========================================================================== */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.externalizeJsonPropsSimple = externalizeJsonPropsSimple;
10
- /**
11
- * Components and their props that should be externalized.
12
- * These are the components that typically receive large JSON objects.
13
- */
14
- const COMPONENTS_TO_EXTERNALIZE = [
15
- { component: "StatusCodes", prop: "responses" },
16
- { component: "ParamsDetails", prop: "parameters" },
17
- { component: "RequestSchema", prop: "body" },
18
- { component: "Schema", prop: "schema" },
19
- { component: "SchemaItem", prop: "schema" },
20
- ];
21
- /**
22
- * Minimum size (in characters) for a JSON prop to be externalized.
23
- * Props smaller than this threshold will remain inline.
24
- */
25
- const MIN_SIZE_THRESHOLD = 500;
26
- /**
27
- * Extracts the content between balanced braces starting at a given position.
28
- * Returns the content (without outer braces) and the end position.
29
- */
30
- function extractBalancedBraces(str, startIndex) {
31
- if (str[startIndex] !== "{") {
32
- return null;
33
- }
34
- let depth = 0;
35
- let inString = false;
36
- let stringChar = "";
37
- let escaped = false;
38
- for (let i = startIndex; i < str.length; i++) {
39
- const char = str[i];
40
- if (escaped) {
41
- escaped = false;
42
- continue;
43
- }
44
- if (char === "\\") {
45
- escaped = true;
46
- continue;
47
- }
48
- if (!inString) {
49
- if (char === '"' || char === "'") {
50
- inString = true;
51
- stringChar = char;
52
- }
53
- else if (char === "{") {
54
- depth++;
55
- }
56
- else if (char === "}") {
57
- depth--;
58
- if (depth === 0) {
59
- // Found the matching closing brace
60
- return {
61
- content: str.substring(startIndex + 1, i),
62
- endIndex: i,
63
- };
64
- }
65
- }
66
- }
67
- else {
68
- if (char === stringChar) {
69
- inString = false;
70
- }
71
- }
72
- }
73
- return null; // Unbalanced braces
74
- }
75
- /**
76
- * Externalizes large JSON props from MDX content.
77
- * Uses brace-counting to correctly handle deeply nested JSON.
78
- *
79
- * @param mdx - The original MDX content
80
- * @param baseFilename - The base filename (without extension) for the MDX file
81
- * @returns The transformed MDX and list of JSON files to write
82
- */
83
- function externalizeJsonPropsSimple(mdx, baseFilename) {
84
- const jsonFiles = [];
85
- let transformedMdx = mdx;
86
- for (const { component, prop } of COMPONENTS_TO_EXTERNALIZE) {
87
- // Find pattern: prop={
88
- const propPattern = new RegExp(`${prop}=\\{`, "g");
89
- let match;
90
- // Keep searching until no more matches (need to restart after each replacement)
91
- let searchStart = 0;
92
- while (true) {
93
- propPattern.lastIndex = searchStart;
94
- match = propPattern.exec(transformedMdx);
95
- if (!match)
96
- break;
97
- const propStart = match.index;
98
- const braceStart = propStart + prop.length + 1; // Position of '{'
99
- // Extract the balanced content
100
- const extracted = extractBalancedBraces(transformedMdx, braceStart);
101
- if (!extracted) {
102
- searchStart = braceStart + 1;
103
- continue;
104
- }
105
- const jsonContent = extracted.content;
106
- const propEnd = extracted.endIndex + 1; // Position after '}'
107
- // Skip small or non-JSON content
108
- const trimmed = jsonContent.trim();
109
- if (jsonContent.length < MIN_SIZE_THRESHOLD ||
110
- trimmed === "undefined" ||
111
- trimmed === "null" ||
112
- trimmed === "true" ||
113
- trimmed === "false" ||
114
- trimmed.startsWith("require(")) {
115
- searchStart = propEnd;
116
- continue;
117
- }
118
- // Check if this is within the target component
119
- // Look backwards for the component tag
120
- const beforeProp = transformedMdx.substring(0, propStart);
121
- const componentTagPattern = new RegExp(`<${component}[\\s\\S]*$`);
122
- if (!componentTagPattern.test(beforeProp)) {
123
- searchStart = propEnd;
124
- continue;
125
- }
126
- // Generate filename
127
- const existingCount = jsonFiles.filter((f) => f.filename.includes(`.${prop}`)).length;
128
- const suffix = existingCount > 0 ? `.${existingCount + 1}` : "";
129
- const jsonFilename = `${baseFilename}.${prop}${suffix}.json`;
130
- // Store the JSON file
131
- jsonFiles.push({
132
- filename: jsonFilename,
133
- content: jsonContent.trim(),
134
- });
135
- // Replace the prop value with require()
136
- const newProp = `${prop}={require("./${jsonFilename}")}`;
137
- transformedMdx =
138
- transformedMdx.substring(0, propStart) +
139
- newProp +
140
- transformedMdx.substring(propEnd);
141
- // Continue searching after the replacement
142
- searchStart = propStart + newProp.length;
143
- }
144
- }
145
- return {
146
- mdx: transformedMdx,
147
- jsonFiles,
148
- };
149
- }
@@ -1,201 +0,0 @@
1
- /* ============================================================================
2
- * Copyright (c) Palo Alto Networks
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- * ========================================================================== */
7
-
8
- /**
9
- * This module provides utilities for externalizing large JSON props in MDX files.
10
- *
11
- * The motivation is to improve MDX compilation performance. When large JSON objects
12
- * are embedded directly as JSX props, MDX needs to parse them into AST and then
13
- * serialize them back. By externalizing to JSON files and using require(), the
14
- * MDX compiler can skip this expensive processing.
15
- *
16
- * @see https://github.com/facebook/docusaurus/discussions/11664
17
- */
18
-
19
- export interface ExternalizedJsonFile {
20
- /** The filename for the JSON file (without path) */
21
- filename: string;
22
- /** The JSON content to write */
23
- content: string;
24
- }
25
-
26
- export interface ExternalizeResult {
27
- /** The transformed MDX content with require() statements */
28
- mdx: string;
29
- /** List of JSON files that need to be written */
30
- jsonFiles: ExternalizedJsonFile[];
31
- }
32
-
33
- /**
34
- * Components and their props that should be externalized.
35
- * These are the components that typically receive large JSON objects.
36
- */
37
- const COMPONENTS_TO_EXTERNALIZE = [
38
- { component: "StatusCodes", prop: "responses" },
39
- { component: "ParamsDetails", prop: "parameters" },
40
- { component: "RequestSchema", prop: "body" },
41
- { component: "Schema", prop: "schema" },
42
- { component: "SchemaItem", prop: "schema" },
43
- ];
44
-
45
- /**
46
- * Minimum size (in characters) for a JSON prop to be externalized.
47
- * Props smaller than this threshold will remain inline.
48
- */
49
- const MIN_SIZE_THRESHOLD = 500;
50
-
51
- /**
52
- * Extracts the content between balanced braces starting at a given position.
53
- * Returns the content (without outer braces) and the end position.
54
- */
55
- function extractBalancedBraces(
56
- str: string,
57
- startIndex: number
58
- ): { content: string; endIndex: number } | null {
59
- if (str[startIndex] !== "{") {
60
- return null;
61
- }
62
-
63
- let depth = 0;
64
- let inString = false;
65
- let stringChar = "";
66
- let escaped = false;
67
-
68
- for (let i = startIndex; i < str.length; i++) {
69
- const char = str[i];
70
-
71
- if (escaped) {
72
- escaped = false;
73
- continue;
74
- }
75
-
76
- if (char === "\\") {
77
- escaped = true;
78
- continue;
79
- }
80
-
81
- if (!inString) {
82
- if (char === '"' || char === "'") {
83
- inString = true;
84
- stringChar = char;
85
- } else if (char === "{") {
86
- depth++;
87
- } else if (char === "}") {
88
- depth--;
89
- if (depth === 0) {
90
- // Found the matching closing brace
91
- return {
92
- content: str.substring(startIndex + 1, i),
93
- endIndex: i,
94
- };
95
- }
96
- }
97
- } else {
98
- if (char === stringChar) {
99
- inString = false;
100
- }
101
- }
102
- }
103
-
104
- return null; // Unbalanced braces
105
- }
106
-
107
- /**
108
- * Externalizes large JSON props from MDX content.
109
- * Uses brace-counting to correctly handle deeply nested JSON.
110
- *
111
- * @param mdx - The original MDX content
112
- * @param baseFilename - The base filename (without extension) for the MDX file
113
- * @returns The transformed MDX and list of JSON files to write
114
- */
115
- export function externalizeJsonPropsSimple(
116
- mdx: string,
117
- baseFilename: string
118
- ): ExternalizeResult {
119
- const jsonFiles: ExternalizedJsonFile[] = [];
120
- let transformedMdx = mdx;
121
-
122
- for (const { component, prop } of COMPONENTS_TO_EXTERNALIZE) {
123
- // Find pattern: prop={
124
- const propPattern = new RegExp(`${prop}=\\{`, "g");
125
- let match;
126
-
127
- // Keep searching until no more matches (need to restart after each replacement)
128
- let searchStart = 0;
129
- while (true) {
130
- propPattern.lastIndex = searchStart;
131
- match = propPattern.exec(transformedMdx);
132
-
133
- if (!match) break;
134
-
135
- const propStart = match.index;
136
- const braceStart = propStart + prop.length + 1; // Position of '{'
137
-
138
- // Extract the balanced content
139
- const extracted = extractBalancedBraces(transformedMdx, braceStart);
140
- if (!extracted) {
141
- searchStart = braceStart + 1;
142
- continue;
143
- }
144
-
145
- const jsonContent = extracted.content;
146
- const propEnd = extracted.endIndex + 1; // Position after '}'
147
-
148
- // Skip small or non-JSON content
149
- const trimmed = jsonContent.trim();
150
- if (
151
- jsonContent.length < MIN_SIZE_THRESHOLD ||
152
- trimmed === "undefined" ||
153
- trimmed === "null" ||
154
- trimmed === "true" ||
155
- trimmed === "false" ||
156
- trimmed.startsWith("require(")
157
- ) {
158
- searchStart = propEnd;
159
- continue;
160
- }
161
-
162
- // Check if this is within the target component
163
- // Look backwards for the component tag
164
- const beforeProp = transformedMdx.substring(0, propStart);
165
- const componentTagPattern = new RegExp(`<${component}[\\s\\S]*$`);
166
- if (!componentTagPattern.test(beforeProp)) {
167
- searchStart = propEnd;
168
- continue;
169
- }
170
-
171
- // Generate filename
172
- const existingCount = jsonFiles.filter((f) =>
173
- f.filename.includes(`.${prop}`)
174
- ).length;
175
- const suffix = existingCount > 0 ? `.${existingCount + 1}` : "";
176
- const jsonFilename = `${baseFilename}.${prop}${suffix}.json`;
177
-
178
- // Store the JSON file
179
- jsonFiles.push({
180
- filename: jsonFilename,
181
- content: jsonContent.trim(),
182
- });
183
-
184
- // Replace the prop value with require()
185
- const newProp = `${prop}={require("./${jsonFilename}")}`;
186
-
187
- transformedMdx =
188
- transformedMdx.substring(0, propStart) +
189
- newProp +
190
- transformedMdx.substring(propEnd);
191
-
192
- // Continue searching after the replacement
193
- searchStart = propStart + newProp.length;
194
- }
195
- }
196
-
197
- return {
198
- mdx: transformedMdx,
199
- jsonFiles,
200
- };
201
- }