Skip to content

feat(plugin): add externalJsonProps option to improve build perf#1279

Merged
sserrata merged 1 commit intomainfrom
feat/external-json-props
Jan 16, 2026
Merged

feat(plugin): add externalJsonProps option to improve build perf#1279
sserrata merged 1 commit intomainfrom
feat/external-json-props

Conversation

@sserrata
Copy link
Member

@sserrata sserrata commented Jan 16, 2026

Description

Add new externalJsonProps configuration option that extracts large JSON props from MDX files into separate .json files loaded via require(). This bypasses MDX AST parsing for large JSON objects (>500 chars), significantly improving build times for large OpenAPI specs.

The option is enabled by default due to the significant performance benefits (up to 25x faster builds in benchmarks). Users can opt-out by setting externalJsonProps: false.

Changes:

  • Add externalizeJsonProps.ts utility for JSON extraction
  • Integrate with doc generation and cleanup flows
  • Add TypeScript types and Joi validation (default: true)
  • Update README with documentation and usage examples
  • Update .gitignore to exclude generated JSON files

Motivation and Context

Large json props can greatly increase build times. See facebook/docusaurus#11664 (reply in thread) for background.

How Has This Been Tested?

Tested using the Harvester OpenAPI spec

@sserrata sserrata self-assigned this Jan 16, 2026
@sserrata sserrata added the enhancement New feature or request label Jan 16, 2026
…prove build performance

Add new externalJsonProps configuration option that extracts large JSON
props from MDX files into separate .json files loaded via require().
This bypasses MDX AST parsing for large JSON objects, significantly
improving build times for large OpenAPI specs.

The option is enabled by default due to the significant performance
benefits (25x faster builds in benchmarks). Users can opt-out by
setting externalJsonProps: false.

Changes:
- Add externalizeJsonProps.ts utility for JSON extraction
- Integrate with doc generation and cleanup flows
- Add TypeScript types and Joi validation (default: true)
- Update README with documentation and usage examples
- Update .gitignore to exclude generated JSON files
@sserrata sserrata force-pushed the feat/external-json-props branch from 412f242 to cb08d69 Compare January 16, 2026 20:48
@github-actions
Copy link

github-actions bot commented Jan 16, 2026

Size Change: +263 B (+0.01%)

Total Size: 2.17 MB

Filename Size Change
demo/build/index.html 92.3 kB +263 B (+0.29%)
ℹ️ View Unchanged
Filename Size
demo/.docusaurus/codeTranslations.json 2 B
demo/.docusaurus/docusaurus.config.mjs 14.7 kB
demo/.docusaurus/globalData.json 60.5 kB
demo/.docusaurus/i18n.json 372 B
demo/.docusaurus/registry.js 87.6 kB
demo/.docusaurus/routes.js 82.8 kB
demo/.docusaurus/routesChunkNames.json 34.3 kB
demo/.docusaurus/site-metadata.json 1.51 kB
demo/build/assets/css/styles.********.css 163 kB
demo/build/assets/js/main.********.js 643 kB
demo/build/assets/js/runtime~main.********.js 20.9 kB
demo/build/petstore/add-pet/index.html 29.3 kB
demo/build/petstore/create-user/index.html 24 kB
demo/build/petstore/create-users-with-array-input/index.html 24.1 kB
demo/build/petstore/create-users-with-list-input/index.html 24.1 kB
demo/build/petstore/delete-order/index.html 23.8 kB
demo/build/petstore/delete-pet/index.html 24.1 kB
demo/build/petstore/delete-user/index.html 24.3 kB
demo/build/petstore/find-pets-by-status/index.html 24.8 kB
demo/build/petstore/find-pets-by-tags/index.html 25.4 kB
demo/build/petstore/get-inventory/index.html 23.1 kB
demo/build/petstore/get-order-by-id/index.html 24.1 kB
demo/build/petstore/get-pet-by-id/index.html 24.9 kB
demo/build/petstore/get-user-by-name/index.html 24.4 kB
demo/build/petstore/login-user/index.html 24.9 kB
demo/build/petstore/logout-user/index.html 23.7 kB
demo/build/petstore/new-pet/index.html 24.3 kB
demo/build/petstore/pet/index.html 22.5 kB
demo/build/petstore/place-order/index.html 23.3 kB
demo/build/petstore/schemas/apiresponse/index.html 24.6 kB
demo/build/petstore/schemas/cat/index.html 38.8 kB
demo/build/petstore/schemas/category/index.html 25.7 kB
demo/build/petstore/schemas/dog/index.html 39 kB
demo/build/petstore/schemas/honeybee/index.html 39.1 kB
demo/build/petstore/schemas/id/index.html 22.7 kB
demo/build/petstore/schemas/order/index.html 26.8 kB
demo/build/petstore/schemas/pet/index.html 38.6 kB
demo/build/petstore/schemas/tag/index.html 24.1 kB
demo/build/petstore/schemas/user/index.html 46.4 kB
demo/build/petstore/store/index.html 21.5 kB
demo/build/petstore/subscribe-to-the-store-events/index.html 30.2 kB
demo/build/petstore/swagger-petstore-yaml/index.html 30.2 kB
demo/build/petstore/update-pet-with-form/index.html 24.3 kB
demo/build/petstore/update-pet/index.html 24.7 kB
demo/build/petstore/update-user/index.html 24.3 kB
demo/build/petstore/upload-file/index.html 24.1 kB
demo/build/petstore/user/index.html 22.2 kB

compressed-size-action

@github-actions
Copy link

github-actions bot commented Jan 16, 2026

Visit the preview URL for this PR (updated for commit cb08d69):

https://docusaurus-openapi-36b86--pr1279-hd8ujlaq.web.app

(expires Sun, 15 Feb 2026 20:52:15 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: bf293780ee827f578864d92193b8c2866acd459f

@sserrata sserrata merged commit 77f891e into main Jan 16, 2026
13 checks passed
@sserrata sserrata deleted the feat/external-json-props branch January 16, 2026 21:47
Copy link
Contributor

@slorber slorber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of manipulating strings using RegExps like this and would have prefered to manipulate the data in a structured way before emitting the markdown content.

But if it worksas-is, that's already nice.


Also wondering how did you define the threshold. Isn't it faster if you always externalize json files?

At the same time, using blocking IOs might slow down code generation if you emit many tiny json files. But not sure it would be a problem if you emitted those files asynchronously.

* @returns The transformed MDX and list of JSON files to write
*/
export function externalizeJsonPropsSimple(
mdx: string,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not super fan of processing MDX as a string here, but hey if that works it's better than nothing.

If you are keen to refactor things, just an idea that could likely produce a more robust implementation:

  • create() return an object containing the string + external files
  • render([]) can accept this new object type and also return it (merging all the external files to create)

sserrata added a commit that referenced this pull request Jan 20, 2026
Implements slorber's suggestion from PR #1279 to handle externalization
at the source (in create()) rather than post-processing MDX strings.

Changes:
- Add externalization context system to utils.ts
- Modify create() to detect large JSON props and externalize inline
- Remove externalizeJsonProps.ts (no longer needed)
- Update index.ts to use context-based approach

This is more architecturally clean as the externalization decision
happens where the component is created, not via regex parsing afterward.
sserrata added a commit that referenced this pull request Jan 21, 2026
* refactor(plugin): externalize JSON props at source in create()

Implements slorber's suggestion from PR #1279 to handle externalization
at the source (in create()) rather than post-processing MDX strings.

Changes:
- Add externalization context system to utils.ts
- Modify create() to detect large JSON props and externalize inline
- Remove externalizeJsonProps.ts (no longer needed)
- Update index.ts to use context-based approach

This is more architecturally clean as the externalization decision
happens where the component is created, not via regex parsing afterward.

* refactor(plugin): drop size threshold for externalization

Always externalize eligible props (responses, parameters, body, schema)
regardless of size. This simplifies the logic and provides consistent
behavior - users either have externalization on or off, no edge cases.

* refactor(plugin): use one JSON file per component with spread props

Implements slorber's suggestion to externalize all props for a component
into a single JSON file and use spread syntax:

  <StatusCodes {...require('./add-pet.StatusCodes.json')} />

Instead of separate files per prop:

  <StatusCodes responses={require('./add-pet.responses.json')} />

Benefits:
- Cleaner file organization (one file per component vs per prop)
- Simpler logic (no need to track which props to externalize)
- All props for a component are colocated in one file

* refactor(plugin): use runWithExternalization wrapper for cleaner API

Provides a cleaner API similar to AsyncLocalStorage pattern but uses
module-level state internally (AsyncLocalStorage isn't available in
browser bundles that Docusaurus creates).

The runWithExternalization() function wraps generation with try/finally
to ensure context is always cleaned up, providing the same safety
guarantees as AsyncLocalStorage.run().
sserrata added a commit that referenced this pull request Jan 27, 2026
Major performance improvement: The new `externalJsonProps` option (enabled by default) dramatically reduces build times and bundle sizes by externalizing large JSON props from MDX files.

- New `externalJsonProps` plugin option significantly improves build performance
- Sticky positioning for the API Explorer right panel improves UX on long API pages
- Dynamic request body updates when switching anyOf/oneOf tabs

#### 🚀 New Feature

- feat(plugin): add externalJsonProps option (enabled by default) to improve build performance (#1279)
- feat(theme): add sticky positioning to API Explorer right panel (#1288)
- feat: dynamically update request body when anyOf/oneOf tab changes (#1287)

#### 🐛 Bug Fix

- fix: render inline enum values in anyOf schemas (#1286)
- fix: generate correct examples for different request content types (#1284)

#### 🏠 Refactoring

- refactor: change plugin and theme types.ts to types.d.ts (#1281)
- refactor: externalize using create() and drop size threshold requirement (#1280)

#### 📝 Documentation

- docs: sync README and intro.mdx with plugin docs

#### 🤖 Dependencies

- chore(deps): bump lodash from 4.17.21 to 4.17.23 (#1282)

#### Committers: 3

- dependabot[bot]
- Ollie Monk
- Steven Serrata
@sserrata sserrata mentioned this pull request Jan 27, 2026
sserrata added a commit that referenced this pull request Jan 27, 2026
Major performance improvement: The new `externalJsonProps` option (enabled by default) dramatically reduces build times and bundle sizes by externalizing large JSON props from MDX files.

- New `externalJsonProps` plugin option significantly improves build performance
- Sticky positioning for the API Explorer right panel improves UX on long API pages
- Dynamic request body updates when switching anyOf/oneOf tabs

#### 🚀 New Feature

- feat(plugin): add externalJsonProps option (enabled by default) to improve build performance (#1279)
- feat(theme): add sticky positioning to API Explorer right panel (#1288)
- feat: dynamically update request body when anyOf/oneOf tab changes (#1287)

#### 🐛 Bug Fix

- fix: render inline enum values in anyOf schemas (#1286)
- fix: generate correct examples for different request content types (#1284)

#### 🏠 Refactoring

- refactor: change plugin and theme types.ts to types.d.ts (#1281)
- refactor: externalize using create() and drop size threshold requirement (#1280)

#### 📝 Documentation

- docs: sync README and intro.mdx with plugin docs

#### 🤖 Dependencies

- chore(deps): bump lodash from 4.17.21 to 4.17.23 (#1282)

#### Committers: 3

- dependabot[bot]
- Ollie Monk
- Steven Serrata
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants