Skip to content

perf: improve perf with more delegation - #1090

Open
avivkeller wants to merge 2 commits into
mainfrom
perf-improvements-1234
Open

perf: improve perf with more delegation#1090
avivkeller wants to merge 2 commits into
mainfrom
perf-improvements-1234

Conversation

@avivkeller

@avivkeller avivkeller commented Sep 6, 2026

Copy link
Copy Markdown
Member

Fixes #1008

  • The html generator builds the component library once and the client assets
    once, then compiles, renders, minifies and writes each page on its own in the
    worker pool, so memory scales with the largest page rather than with the
    site. On the Node.js API docs with section pages, peak memory drops from
    about 8 GB to 3.4 GB and the build runs in half the time.
  • all.html is assembled from the module pages' compiled content, in sidebar
    order, instead of being built again from every module. The option moved from
    jsx-ast.generateAllPage to html.generateAllPage, and the page shows no
    reading time.
  • jsx-ast emits { data, headings, readingTime, content } per page,
    content being the page body as one JSX fragment; html wraps it in
    <Layout>.
  • The WebBundler contract is now buildServer / compile / buildClient
    (see the html README). Vite plugins no longer see the HTML pages; customize
    them through the template, whose ${entrypoint} is replaced by ${assets}.
    Synthetic pages load assets from the site root, so 404.html works at nested
    paths.
  • @doc-kit/core: getRemarkRehypeWithShiki moved to
    @doc-kit/core/utils/remark-shiki.mjs and typeAnnotationToHighlightedHast
    to @doc-kit/core/utils/type-annotations/highlighted.mjs, so the ast and
    metadata stages no longer load Shiki. The default threads is capped at 4.
    Function-valued generator configuration (such as a custom bundler) is
    dropped before it is sent to workers instead of failing the structured clone.

@avivkeller
avivkeller requested a review from a team as a code owner September 6, 2026 02:12
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 6, 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: Failed ❌

View logs ↗
3ef2d77 2026-09-07T22:09:13.686Z View logs ↗
  • Build: Failed ❌

View logs ↗
2de8cdb 2026-09-06T02:12:54.801Z View logs ↗

@vercel

vercel Bot commented Sep 6, 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 Sep 7, 2026 10:09pm UTC

Request Review

Comment thread packages/react/src/html/utils/generate.mjs Fixed
Comment thread packages/react/src/html/utils/generate.mjs Fixed
@codecov

codecov Bot commented Sep 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.29545% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.70%. Comparing base (307fdc9) to head (3ef2d77).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
packages/react/src/html/bundlers/vite.mjs 95.39% 7 Missing ⚠️
packages/react/src/jsx-ast/generate.mjs 76.92% 3 Missing ⚠️
packages/core/src/utils/configuration/index.mjs 85.71% 1 Missing ⚠️
...es/core/src/utils/type-annotations/highlighted.mjs 98.43% 1 Missing ⚠️
packages/react/src/html/utils/generate.mjs 98.93% 1 Missing ⚠️
packages/react/src/html/utils/processing.mjs 98.33% 1 Missing ⚠️
packages/react/src/html/utils/render.mjs 98.93% 1 Missing ⚠️
packages/react/src/jsx-ast/utils/buildContent.mjs 97.14% 1 Missing ⚠️
...es/react/src/jsx-ast/utils/plugins/transformer.mjs 91.66% 1 Missing ⚠️
packages/react/src/jsx-ast/utils/remark.mjs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1090      +/-   ##
==========================================
+ Coverage   90.60%   90.70%   +0.09%     
==========================================
  Files         217      220       +3     
  Lines       20802    21132     +330     
  Branches     1974     1991      +17     
==========================================
+ Hits        18847    19167     +320     
- Misses       1948     1958      +10     
  Partials        7        7              

☔ 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.

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

api-links Generator

Performance estimate (single CI run)

  • Generation time: 21.6% slower (1.02 s → 1.24 s)
  • Peak memory: 20.2% higher (359.77 MB → 432.58 MB)

legacy-html Generator

Output size: 1 file changed · net +86.00 B

File size details
File Main PR Change
assets/style.css 18.10 KB 18.18 KB +86.00 B (+0.5%)

Performance estimate (single CI run)

  • Generation time: 8.6% faster (35.62 s → 32.56 s)
  • Peak memory: 7.6% lower (2.55 GB → 2.36 GB)

legacy-json Generator

Performance estimate (single CI run)

  • Generation time: 61.5% faster (21.72 s → 8.37 s)
  • Peak memory: 23.3% lower (2.05 GB → 1.57 GB)

llms-txt Generator

Performance estimate (single CI run)

  • Generation time: 65.7% faster (21.63 s → 7.42 s)
  • Peak memory: 16.9% lower (1.80 GB → 1.50 GB)

orama-db Generator

Output size: 1 file changed · net -4.00 B

File size details
File Main PR Change
orama-db.json 9.36 MB 9.36 MB -4.00 B (-0.0%)

Performance estimate (single CI run)

  • Generation time: 63.3% faster (20.69 s → 7.60 s)
  • Peak memory: 23.6% lower (2.04 GB → 1.56 GB)

web Generator

Output size: 2 files changed · net -49.00 B

File size details
File Main PR Change
all.html 32.46 MB 32.46 MB -47.00 B (-0.0%)
404.html 21.90 KB 21.90 KB -2.00 B (-0.0%)

Performance estimate (single CI run)

  • Generation time: 66.7% faster (130.11 s → 43.29 s)
  • Peak memory: 28.5% lower (5.50 GB → 3.94 GB)

@ovflowd

ovflowd commented Sep 6, 2026

Copy link
Copy Markdown
Member

Various performance improvements

Could you expand what these "various performance improvements" are? The PR descriptions should clearly state what the PR does.

Comment thread packages/core/src/utils/configuration/index.mjs Outdated
Comment thread packages/react/src/jsx-ast/generate.mjs Outdated
@ovflowd

ovflowd commented Sep 6, 2026

Copy link
Copy Markdown
Member

@nodejs/platform-riscv64 could you maybe break down this PR after looking at it for a while it feels that it is doing several things at the same time, which makes it harder to understand what is what (ie: what is being changed for performance improv and which piece of that, what is just refactoring) [...] imo even just the piece of threading tuning should be its own PR.

Reviewing such large PRs is hard and reduces my ability (and of others) to properly review this PR.

Comment thread packages/react/src/html/bundlers/vite.mjs
Comment thread packages/react/src/html/bundlers/vite.mjs
Comment thread packages/react/src/html/bundlers/vite.mjs
Comment thread packages/react/src/html/bundlers/vite.mjs Outdated
Comment thread packages/react/src/html/bundlers/vite.mjs Outdated
Comment thread packages/react/src/html/utils/all.mjs Outdated
const byApi = new Map(pages.map(page => [page.data.api, page]));

const parts = getSortedHeadNodes(
pages

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.

Could you assign this to its own const, making this line simpler?

Comment thread packages/react/src/html/utils/all.mjs Outdated
.filter(data => !data.synthetic && !data.chunk && data.api !== 'index')
).map(({ api }) => byApi.get(api));

const minutes = parts.reduce(

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.

I'd argue we shouldn't have a reading minutes on our API docs, only on Learn/Blog content. Nor should we have it on all.html

...imports,
`export const headings = ${JSON.stringify(headings)};`,
`export const content = () => ${content};`,
`export default () => renderToStringAsync(<${JSX_IMPORTS.Layout.name} metadata={${JSON.stringify(metadata)}} headings={headings} readingTime={${JSON.stringify(readingTime?.text)}}>{content()}</${JSX_IMPORTS.Layout.name}>);`,

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.

Agree, I'd aruge sanitization would be good here

* @returns {string}
*/
export const buildAssetTags = ({ scripts, preloads, stylesheets }, root) =>
[

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.

Could this not be a multi level spread? Also is string manipulation the best way of doing this?

@bmuenzenmeyer

Copy link
Copy Markdown
Contributor

Could you expand what these "various performance improvements" are? The PR descriptions should clearly state what the PR does.

I'd maybe even go further and state that we need to slow down and do this in chunks.

You are doing good good work to reduce memory in pursuit of nodejs/node#62045 but it's hard to keep up with the pace

@avivkeller

Copy link
Copy Markdown
Member Author

I've updated the description for more information,

  • The html generator builds the component library once and the client assets
    once, then compiles, renders, minifies and writes each page on its own in the
    worker pool, so memory scales with the largest page rather than with the
    site. On the Node.js API docs with section pages, peak memory drops from
    about 8 GB to 3.4 GB and the build runs in half the time.
  • all.html is assembled from the module pages' compiled content, in sidebar
    order, instead of being built again from every module. The option moved from
    jsx-ast.generateAllPage to html.generateAllPage, and the page shows no
    reading time.
  • jsx-ast emits { data, headings, readingTime, content } per page,
    content being the page body as one JSX fragment; html wraps it in
    <Layout>.
  • The WebBundler contract is now buildServer / compile / buildClient.
    Vite plugins no longer see the HTML pages; customize
    them through the template, whose ${entrypoint} is replaced by ${assets}.
    Synthetic pages load assets from the site root, so 404.html works at nested
    paths.
  • @doc-kit/core: getRemarkRehypeWithShiki moved to
    @doc-kit/core/utils/remark-shiki.mjs and typeAnnotationToHighlightedHast
    to @doc-kit/core/utils/type-annotations/highlighted.mjs, so the ast and
    metadata stages no longer load Shiki. The default threads is capped at 4.
    Function-valued generator configuration (such as a custom bundler) is
    dropped before it is sent to workers instead of failing the structured clone.

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.

Make all.html a concatenation of existing pages

4 participants