Repository navigation
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
2 of 5 tasks
🚀 Deploying Preview to Cloudflare 🚀Preview Deployments by commit
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1205 +/- ##
==========================================
+ Coverage 93.77% 94.34% +0.57%
==========================================
Files 289 296 +7
Lines 28813 31922 +3109
Branches 2913 3241 +328
==========================================
+ Hits 27018 30116 +3098
- Misses 1773 1784 +11
Partials 22 22 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Contributor
|
| File | Main | PR | Change |
|---|---|---|---|
addons.html |
374.23 KB | 328.87 KB | -45.36 KB (-12.1%) |
embedding.html |
64.38 KB | 57.78 KB | -6.60 KB (-10.3%) |
async_context.html |
247.17 KB | 245.10 KB | -2.08 KB (-0.8%) |
tls.html |
297.30 KB | 298.85 KB | +1.54 KB (+0.5%) |
n-api.html |
822.59 KB | 822.23 KB | -364.00 B (-0.0%) |
module.html |
364.54 KB | 364.76 KB | +224.00 B (+0.1%) |
readline.html |
219.10 KB | 219.27 KB | +168.00 B (+0.1%) |
worker_threads.html |
417.80 KB | 417.64 KB | -168.00 B (-0.0%) |
repl.html |
226.64 KB | 226.75 KB | +112.00 B (+0.0%) |
test.html |
1009.73 KB | 1009.67 KB | -56.00 B (-0.0%) |
Performance estimate (single CI run)
- Generation time: 5.8% faster (16.34 s → 15.39 s)
- Peak memory: 12.4% lower (1.50 GB → 1.31 GB)
legacy-json Generator
Performance estimate (single CI run)
- Generation time: 5.6% faster (9.43 s → 8.90 s)
- Peak memory: 5.1% lower (907.74 MB → 861.82 MB)
llms-txt Generator
Performance estimate (single CI run)
- Generation time: 34.0% slower (6.18 s → 8.28 s)
- Peak memory: 9.4% lower (934.21 MB → 846.34 MB)
orama-db Generator
Performance estimate (single CI run)
- Generation time: 70.4% slower (5.20 s → 8.86 s)
- Peak memory: 11.8% lower (1.11 GB → 1004.27 MB)
web Generator
Output size: 46 files changed · net -34.83 MB
File size details
| File | Main | PR | Change |
|---|---|---|---|
all.html |
34.78 MB | — | -34.78 MB (-100.0%) |
addons.html |
389.73 KB | 361.93 KB | -27.80 KB (-7.1%) |
embedding.html |
73.02 KB | 68.39 KB | -4.63 KB (-6.3%) |
process.html |
1.20 MB | 1.19 MB | -4.16 KB (-0.3%) |
child_process.html |
706.47 KB | 704.23 KB | -2.24 KB (-0.3%) |
tls.html |
550.15 KB | 552.33 KB | +2.18 KB (+0.4%) |
fs.html |
2.36 MB | 2.35 MB | -1.32 KB (-0.1%) |
crypto.html |
2.03 MB | 2.03 MB | -1.21 KB (-0.1%) |
assert.html |
673.00 KB | 671.89 KB | -1.11 KB (-0.2%) |
http.html |
1.23 MB | 1.23 MB | -1.06 KB (-0.1%) |
util.html |
1.26 MB | 1.26 MB | -964.00 B (-0.1%) |
async_hooks.html |
293.14 KB | 292.26 KB | -898.00 B (-0.3%) |
http2.html |
1.31 MB | 1.31 MB | -788.00 B (-0.1%) |
readline.html |
380.82 KB | 380.05 KB | -788.00 B (-0.2%) |
buffer.html |
1.82 MB | 1.82 MB | -736.00 B (-0.0%) |
dgram.html |
313.49 KB | 312.78 KB | -728.00 B (-0.2%) |
vm.html |
656.77 KB | 656.11 KB | -680.00 B (-0.1%) |
webstreams.html |
565.36 KB | 564.70 KB | -674.00 B (-0.1%) |
perf_hooks.html |
910.85 KB | 910.24 KB | -620.00 B (-0.1%) |
cluster.html |
317.74 KB | 317.14 KB | -616.00 B (-0.2%) |
worker_threads.html |
662.16 KB | 661.58 KB | -588.00 B (-0.1%) |
zlib.html |
1.00 MB | 1023.86 KB | -504.00 B (-0.0%) |
test.html |
1.47 MB | 1.47 MB | -458.00 B (-0.0%) |
ffi.html |
204.00 KB | 203.56 KB | -448.00 B (-0.2%) |
globals.html |
302.35 KB | 301.96 KB | -394.00 B (-0.1%) |
vfs.html |
213.64 KB | 213.31 KB | -340.00 B (-0.2%) |
url.html |
533.89 KB | 533.56 KB | -338.00 B (-0.1%) |
stream_iter.html |
795.19 KB | 794.90 KB | -296.00 B (-0.0%) |
v8.html |
574.47 KB | 574.19 KB | -294.00 B (-0.0%) |
async_context.html |
329.94 KB | 329.71 KB | -234.00 B (-0.1%) |
n-api.html |
1.05 MB | 1.05 MB | -230.00 B (-0.0%) |
dns.html |
442.03 KB | 441.81 KB | -224.00 B (-0.0%) |
sqlite.html |
542.98 KB | 542.81 KB | -174.00 B (-0.0%) |
domain.html |
131.23 KB | 131.06 KB | -168.00 B (-0.1%) |
quic.html |
985.72 KB | 985.56 KB | -168.00 B (-0.0%) |
stream.html |
1.27 MB | 1.27 MB | -122.00 B (-0.0%) |
repl.html |
314.01 KB | 313.90 KB | -116.00 B (-0.0%) |
modules.html |
238.01 KB | 237.90 KB | -112.00 B (-0.0%) |
tty.html |
113.74 KB | 113.63 KB | -112.00 B (-0.1%) |
module.html |
533.05 KB | 532.96 KB | -84.00 B (-0.0%) |
bench.html |
213.44 KB | 213.38 KB | -60.00 B (-0.0%) |
cli.html |
678.78 KB | 678.73 KB | -56.00 B (-0.0%) |
permissions.html |
94.66 KB | 94.60 KB | -56.00 B (-0.1%) |
esm.html |
196.06 KB | 196.05 KB | -8.00 B (-0.0%) |
single-executable-applications.html |
170.64 KB | 170.64 KB | -2.00 B (-0.0%) |
wasi.html |
83.98 KB | 83.98 KB | -2.00 B (-0.0%) |
Performance estimate (single CI run)
- Generation time: 27.5% faster (25.60 s → 18.57 s)
- Peak memory: 0.2% lower (1.55 GB → 1.55 GB)
ovflowd
force-pushed
the
perf/stream-dependencies
branch
from
October 31, 2026 13:25
482ec49 to
3792e19
Compare
Every dependency edge was a full barrier: `cache.consume` collected all
of a generator's chunks before its dependent started, so `metadata`
waited for every file to be parsed, `jsx-ast` for all of the metadata,
and `html` for every page. Chunks only streamed within a generator.
A generator can now set `streamsInput: true` to receive its dependency
as an async iterable of chunks, read as they are produced. With one
thread nothing runs alongside, so it still gets one array. `metadata`,
`section-pages`, `jsx-ast` and `html` stream:
- The cache records a streamed generator's chunks once for all its
readers, and lets go of each chunk once every reader has read it.
- `worker.stream` takes batches as they arrive, starts a few chunks per
thread at a time so generators running at once share the pool, and
yields in submission order. Piscina now runs with `stricterFIFO`: by
default it puts a task it can't hand out at the back of its queue, so
early heavy tasks ran last.
- `jsx-ast` yields pages as they are built, and a `{ manifest }` with
every page's head as soon as all have arrived. `html` bundles on it
and renders pages as they are compiled, `all.html` once its parts are.
- Generators share the pieces: `toBatches` takes an input whole or in
batches, `merge` yields from several async iterables as items arrive,
and `createChannel`, `createReplay` and `createSignal` live in the
core utils.
The output on Node.js's docs is byte-identical at 1 to 8 threads.
Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
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>
`runGenerators` collected every requested generator's output into the
arrays it returns, though neither `generate` nor `preview` reads them.
With `{ results: false }` it drains them instead: each generator still
runs to its end, and once nothing else reads its output, its chunks go
as they are recorded. Both commands now use it.
Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
`all.html`'s program built every module's elements before rendering
any of them: `<>{content0()}{content1()}…</>` calls all 68 content
functions up front. Each part now renders through a component that
creates its elements as it renders and returns a single element, which
render-to-string doesn't keep on its parent the way it keeps a
fragment's children, so each module's tree can go once it's rendered.
On Node.js's docs the worker rendering `all.html` went from 330-388MB
to 176-189MB live, and peak RSS dropped ~140MB. The output is
identical, ids included, as `useId` counts in the same order. A
composed page also no longer scans its program for component names,
which would take the wrappers for configured components of that name.
Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
Each grammar costs every thread its own memory, about 60MB for JavaScript's on Oniguruma, and time to register. Types and signatures are already highlighted as TypeScript, whose grammar highlights JavaScript too, so `js`, `mjs`, `cjs` and `javascript` code is now highlighted with it by default, and C++ (`cpp`, `c++`) with C's. On Node.js's docs that saves ~190MB of peak memory and 2-7% of build time. In the light theme, the default, JavaScript comes out the same but for one block of console output marked as `js`. Nord, the dark theme, has a few colors only for JavaScript's scopes. C++-only keywords lose their color. `langAlias` takes precedence, and doc-kit now resolves aliases itself: Shiki replaced configured aliases with a grammar's own as it registered. Aliases now also lead on through other aliases and are found in any case. Transformers, and the language `resolveLanguage` returns, are the one code is highlighted as. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
ovflowd
force-pushed
the
perf/stream-dependencies
branch
from
October 31, 2026 13:36
3792e19 to
36b4da5
Compare
With generators streaming into each other, more threads now pay off. On Node.js's docs, medians of 5 runs, the site build took 7.64s at 4 threads, 6.48s at 6 and 6.66s at 8, peaking at 1.9GB, 2.3GB and 2.7GB. The legacy build kept getting faster at 8, but by only another 5% for 320MB more. Machines with fewer CPUs are unaffected, as the default is the CPU count up to this ceiling. Keeping Rolldown and Rayon at 4 threads while doc-kit used 6 or 8 was slower and saved no memory, so the bundler still gets doc-kit's count. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
ovflowd
force-pushed
the
perf/stream-dependencies
branch
from
October 31, 2026 14:44
5a9b862 to
eaecb85
Compare
all.html, every module page's content on one page, is Node.js's, but html built it in, behind `generateAllPage`, and the streaming work spread its bookkeeping through generate.mjs. It's now a generator of @node-core/doc-kit, `html-all`, spliced between jsx-ast and html the way section-pages is between metadata and jsx-ast. It passes the pages on, adds all.html's metadata to the manifest, and right after the last module page adds all.html as a page made of theirs, in sidebar order. html only knows that an item with `parts`, the pages before it that it's made of, is compiled from their content and rendered first; `generateAllPage` goes away. Built with the Node.js preset and the `html-all` target, the Node.js API docs come out byte-identical, all.html included, at 1 and 4 threads. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
ovflowd
force-pushed
the
perf/stream-dependencies
branch
from
October 31, 2026 15:46
eaecb85 to
e585611
Compare
ovflowd
marked this pull request as ready for review
October 31, 2026 16:29
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This PR lets generators start on their dependency's first chunk instead of waiting for all of it, and brings the memory that costs back down. On Node.js's docs the site build gets 22–30% faster and the legacy one 9–10%, with the same output.
Every dependency edge was a full barrier:
cache.consumecollected all of a generator's chunks before its dependent started, sometadatawaited for every file to be parsed,jsx-astfor all of the metadata, andhtmlfor every page. That's also why more threads barely helped:mainonly goes from 9.55s at 4 threads to 8.82s at 6.streamsInput: true, a generator gets its dependency as an async iterable of chunks, read as they're produced. With one thread nothing runs alongside, so it still gets one array.metadata,section-pages,jsx-astandhtmlstream. The cache records a streamed generator once for all its readers, and lets go of each chunk once every reader has read it.worker.streamtakes batches as they arrive, starts a few chunks per thread at a time (CHUNKS_PER_THREAD, 3) so generators running at once share the pool, and yields in submission order. Piscina now runs withstricterFIFO: by default it puts a task it can't hand out at the back of its queue, so early heavy tasks like module pages ran last. The pieces live in the core utils for any generator to use:toBatchestakes an input whole or in batches,mergeyields from several async iterables as items arrive, andcreateChannel,createReplayandcreateSignalare the queue, the recording and the wake-up they're built on.jsx-ast's output order: pages now come out as they're built, in the order they arrived, plus a{ manifest }item with every page's head once all have arrived.htmlbundles as soon as the manifest arrives and renders pages as they're compiled, and a page made of others (one withparts) once its parts are. To list pages as the site does,headingSortFnis now exported at@doc-kit/generator-react/jsx-ast/utils/getSortedHeadNodes. Bundler adapters'compilecan now run before and alongsidebuildServerandbuildClient(the html README says so).metadatadropspositionfrom every node once it's parsed: ~480k objects, ~80MB, and builds ~5% faster.json-simplewrites the metadata as it is, so it no longer has them either and is a third of its size.runGenerators(configuration, { results: false })keeps none of the requested generators' output, and the CLI uses it.all.html, renders one part at a time, rather than building every part's elements first. The worker renderingall.htmlwent from 330–388MB to 176–189MB live.js,mjs,cjs,javascript) is now highlighted with TypeScript's grammar by default, as types already are, and C++ with C's. JavaScript's grammar alone is ~60MB on every thread. In the light theme JavaScript comes out the same, except one block of console output marked asjs; Nord, the dark theme, loses a few colors it only gives JavaScript's scopes, and C++-only keywords lose theirs.langAliastakes precedence, so a grammar inlangsfor one of these languages needs an alias to it. doc-kit now resolves aliases itself, since Shiki replaced configured ones with a grammar's own as it registered; aliases also follow other aliases and match in any case.resolveLanguage, and transformers, give the language code is highlighted as (typescriptformjs).all.htmlmoves to a Node.js generator: it's every module page on one page, so it's Node.js's, buthtmlbuilt it in behindgenerateAllPage, and streaming spread its bookkeeping throughhtml. That option goes away, and@node-core/doc-kitgets anhtml-allgenerator, spliced betweenjsx-astandhtmlthe waysection-pagesis betweenmetadataandjsx-ast. It passes the pages on, addsall.html's head to the manifest, and right after the last module page addsall.htmlas a page made of theirs, in sidebar order. Allhtmlknows is that a page withpartsis compiled from its parts' content and rendered first. Builds that wantall.htmladd thehtml-alltarget, as CI's e2e build now does (-t web -t html-all); Node.js's config (doc: move to redesign node#66713) needs it intargettoo. This is a breaking change for@doc-kit/generator-react.DEFAULT_MAX_THREADSgoes from 4 to 6. 8 threads were no faster than 6 for the site build, and used ~15% more memory. Machines with fewer CPUs, like CI's 4, are unaffected. Rolldown and Rayon keep getting doc-kit's thread count: holding them at 4 was slower and saved no memory.MALLOC_ARENA_MAX=2saved another ~100MB, but glibc only reads it at startup. Re-executing the CLI with it brokenode --watch, so it's left out: Node.js'sMakefileis a better place to set it.Validation
node --run test(845/845),node --run lintandnode --run format:checkare clean, and so is each commit on its own. New tests fail without their change: the replay and the cache's trimming (several readers, with GC checks),worker.stream's limit and refill,groupWholeModules,merge, the channel, the streamed and one-thread inputs ofsection-pages,jsx-ast,html-allandhtml(the manifest,all.htmlcompiled early, failures leaving no temporary files, pages not held on Node.js 24), draining, dropped positions,all.html's ids, and the highlighter's aliases.With the default aliases off, every target builds byte-identically to
mainat-p 1and-p 4, withhtml-alladded whereverall.htmlwas built: the site,legacy-html-allandlegacy-json-all,html,web,json-all,llms-txt-full,sitemap,legacy-html,legacy-json,man-page,addon-verifyandapi-links. The only differences arejson-simple(positions) and the<api>.jsonfilesjsonandlegacy-jsonboth write, which already race onmain.Node.js's docs on Node.js 24, medians of 5 interleaved runs. The site is
orama-db,json,llms-txtandsection-pages(which pulls injsx-astandhtml), legacy islegacy-html-allandlegacy-json-all:mainThe site build also uses less CPU (52.3s to 49.8s at 6 threads).
The new default, on this PR, against thread counts and with Rolldown and Rayon held at 4:
-p 1with--max-old-space-size=512passes 3/3 for both, at ~1.0GB and ~0.9GB peak RSS.Related Issues
Refs: nodejs/nodejs.org#9253, which caches tokens for both themes; doc-kit gets it once it's released.
Check List
node --run testand all tests passed.node --run format:check&node --run lint.