Skip to content

perf(ext/node): convert net, _tls_common, _tls_wrap to lazy-loaded JS#33997

Merged
bartlomieju merged 1 commit into
mainfrom
perf/lazy-load-net-tls-readline-repl
May 12, 2026
Merged

perf(ext/node): convert net, _tls_common, _tls_wrap to lazy-loaded JS#33997
bartlomieju merged 1 commit into
mainfrom
perf/lazy-load-net-tls-readline-repl

Conversation

@bartlomieju
Copy link
Copy Markdown
Member

Follow-up to #33988. Moves the node:_tls_common, node:_tls_wrap, and
node:net polyfills off the eager-evaluated ESM path by rewriting their
sources as IIFEs registered under lazy_loaded_js, with thin
_esm.ts/_esm.js wrappers in lazy_loaded_esm re-exporting the named
members.

net.ts reads Duplex from node:stream and ADDRCONFIG/lookup
from node:dns at module top level, so net_esm.ts adds side-effect
imports of both — without them, the lazy createLazyLoader calls hit a
not-yet-loaded module when _http_agent.js pulls node:net very early
during 01_require.js instantiation. For the same reason
internal/tty.js's top-level ObjectSetPrototypeOf(WriteStream..., lazyNet().Socket.prototype) is deferred into an
ensureWriteStreamPrototype() helper called from the WriteStream
constructor, so it never reaches into net_esm.ts's Socket export
while that wrapper is still mid-evaluation. _readline.mjs and
node:readline = readline.ts also relocate from esm to
lazy_loaded_esm without IIFE conversion, since repl.ts still
statically imports _readline.mjs. The node:repl conversion is left
for a follow-up: repl.ts reads Module, process, Console, etc.
at module top level, and the only IIFE-compatible workaround is a
Proxy-based deferred accessor that I'd rather not introduce.

Continues the work from #33988 by moving the `node:_tls_common`,
`node:_tls_wrap`, and `node:net` polyfills off the eager-evaluated ESM
path. `_tls_common.ts`, `_tls_wrap.js`, and `net.ts` are rewritten as
IIFEs registered under `lazy_loaded_js`, with thin `_esm.ts`/`_esm.js`
wrappers in `lazy_loaded_esm` re-exporting the named members.

`net_esm.ts` adds side-effect imports of `node:stream` and `node:dns`
so that `net.ts`'s top-level `createLazyLoader(...)()` calls hit
already-evaluated modules rather than failing during 01_require.js
instantiation.

`internal/tty.js`'s top-level `ObjectSetPrototypeOf(WriteStream...,
lazyNet().Socket.prototype)` calls move into an `ensureWriteStreamPrototype()`
helper invoked from the WriteStream constructor; this avoids reaching
into `net_esm.ts`'s `export const Socket` binding while the wrapper
module is still mid-evaluation.

`_readline.mjs` and `node:readline = readline.ts` also relocate from
`esm` to `lazy_loaded_esm` (no IIFE conversion since `repl.ts` still
statically imports `_readline.mjs`).
@bartlomieju bartlomieju merged commit a567a4e into main May 12, 2026
136 checks passed
@bartlomieju bartlomieju deleted the perf/lazy-load-net-tls-readline-repl branch May 12, 2026 14:59
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.

1 participant