Skip to content

perf: drop node positions after metadata - #1236

Open
ovflowd wants to merge 2 commits into
mainfrom
perf/drop-positions
Open

ovflowd wants to merge 2 commits into
mainfrom
perf/drop-positions

Conversation

@ovflowd

@ovflowd ovflowd commented Nov 3, 2026

Copy link
Copy Markdown
Member

Description

This PR drops position from every node of metadata's entries once they're parsed. Nothing after metadata reads where a node came from in the source: the type warnings that print positions run while it parses. On Node.js's docs that's ~480k objects, about 80MB, that the main thread no longer holds or copies to and from the workers, and builds are ~5% faster.

json-simple writes the metadata as it is, so it no longer has them either, and is about a third of its size.

Validation

Added drops where each node of an entry came from, which checks that no node of a parsed entry has a position. json-simple is the only target whose output changes, and the <api>.json files json and legacy-json both write already race on main.

Related Issues

Split out of #1205.

Check List

  • I have read the Contributing Guidelines and made commit messages that follow the guideline.
  • I have run node --run test and all tests passed.
  • I have check code formatting with node --run format:check & node --run lint.
  • I've covered new added functionality with unit tests if necessary.

@vercel

vercel Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
api-docs-tooling Ready Ready Preview Nov 4, 2026 6:24pm UTC

Request Review

@codecov

codecov Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.85714% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.13%. Comparing base (9d02e94) to head (b88b1d5).

Files with missing lines Patch % Lines
...generators/metadata/utils/__tests__/parse.test.mjs 90.90% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1236      +/-   ##
==========================================
- Coverage   94.14%   94.13%   -0.01%     
==========================================
  Files         291      291              
  Lines       28908    28950      +42     
  Branches     2936     2943       +7     
==========================================
+ Hits        27214    27253      +39     
- Misses       1672     1675       +3     
  Partials       22       22              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ovflowd ovflowd mentioned this pull request Nov 3, 2026
4 tasks done
@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

api-links Generator

Performance estimate (single CI run)

  • Generation time: 6.6% faster (910.00 ms → 850.00 ms)
  • Peak memory: 0.1% lower (281.43 MB → 281.17 MB)

json Generator

Performance estimate (single CI run)

  • Generation time: 17.9% faster (6.94 s → 5.70 s)
  • Peak memory: 6.7% lower (971.12 MB → 906.23 MB)

legacy-html Generator

Performance estimate (single CI run)

  • Generation time: 14.1% faster (16.32 s → 14.02 s)
  • Peak memory: 2.2% lower (1.44 GB → 1.41 GB)

legacy-json Generator

Performance estimate (single CI run)

  • Generation time: 7.5% faster (6.76 s → 6.25 s)
  • Peak memory: 8.1% lower (958.73 MB → 881.39 MB)

llms-txt Generator

Performance estimate (single CI run)

  • Generation time: 21.0% faster (9.60 s → 7.58 s)
  • Peak memory: 3.4% lower (891.98 MB → 861.62 MB)

orama-db Generator

Performance estimate (single CI run)

  • Generation time: 27.5% slower (6.37 s → 8.12 s)
  • Peak memory: 11.8% lower (1020.07 MB → 899.24 MB)

web Generator

Performance estimate (single CI run)

  • Generation time: 43.6% slower (15.20 s → 21.82 s)
  • Peak memory: 4.1% lower (1.53 GB → 1.46 GB)

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅

View logs ↗
No Preview URL. Enable ↗ b88b1d5 2026-11-04T18:24:21.010Z View logs ↗
  • Build: Success ✅

View logs ↗
No Preview URL. Enable ↗ b65c0b7 2026-11-03T19:34:31.832Z View logs ↗

@ovflowd
ovflowd marked this pull request as ready for review November 3, 2026 20:39
@ovflowd
ovflowd requested a review from a team as a code owner November 3, 2026 20:39

@kgeminicdev kgeminicdev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Checked the "nothing after metadata reads it" claim: the only reader is the invalid-type warning in resolveTypes.mjs, which parseApiDoc runs before this loop, and callIfBefore in utils/unist.mjs is only called from tests. So nothing downstream loses anything.

Nit: heading and stability are both inside content already. The subtree slice starts at the heading's index, and the stability blockquote is found in that same subtree and mutated in place, never removed. So visit(content, …) alone covers all three and skips two extra walks per entry.

Comment on lines +158 to +169
// Nothing after this reads where a node came from: letting it go saves
// memory, and copying it to and from the worker threads
for (const { content, heading, stability } of metadataCollection) {
for (const root of [content, heading, stability]) {
if (root) {
visit(root, node => {
delete node.position;
});
}
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is there a more intuitive way to do this (a double for loop w/ an if seems annoying, iMO)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah, that was clunky. It's a single visit(content) per entry now, since the heading and stability are already part of the content. The test checks those two as well.

Nothing after `metadata` reads where a node came from in the source:
the type warnings that print positions run while it parses. Dropping
them shrinks every entry the main thread holds and copies to and from
the workers. On Node.js's docs that is ~480k position objects, about
80MB, and builds are ~5% faster.

`json-simple`, which writes the metadata as it is, no longer has them
either, and is about a third of its size.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
Assisted-by: Claude Sonnet 5.5 <noreply@anthropic.com>
An entry's heading and stability are nodes of its content, so walking
the content alone covers them, without a loop over the three of them
and a check for a missing one. The test now checks the heading and the
stability too.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
@ovflowd

ovflowd commented Nov 4, 2026

Copy link
Copy Markdown
Member Author

Thanks for double checking who reads positions! Went with your nit, it's one walk over each entry's content now.

This branch was successfully deployed

1 active deployment
Preview – api-docs-tooling — b88b1d5b Deployed Nov 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants