refactor(hot): give each transport its own file, load only the one chosen - #2452
Merged
Merged
Conversation
🦋 Changeset detectedLatest commit: 9959f95 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
`createEventStream` moves out of `hot.js` into `src/servers/EventSourceServer.js`, beside the WebSocket one, and both are required where the transport is picked rather than at the top of the module. Neither is reached until a `createHot` call asks for it: transport EventSourceServer WebSocketServer ws "sse" (default) loaded — — "ws" — loaded loaded your own — — — Previously the event stream was parsed by every consumer, including one on a WebSocket or on a transport of its own, because it lived in `hot.js`. `src/servers/` now holds both, which is the shape webpack-dev-server's `lib/servers/` has, and the CORS and token rules sit with the transport that enforces them — `hot.js` keeps only the default each one starts from and the mint that hands a token to both. `createEventStream` is still exported from `hot.js`, as a wrapper that loads the module on the first call, so an importer sees no change. `requireServer` spells each path out rather than building one from its argument: a bundler has to be able to see both statically. Also fixes two fixtures that hand-wire a client and so have to carry a token of their own, which is what a developer wiring their own entry has to do: the worker app, and the cross-origin test that builds its own WebSocket url — that one reads `instance.token` rather than hardcoding one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
Follow-up to extracting the event stream: the module boundaries were right but three things were still on the wrong side of them. Each transport's CORS default moves to the transport that applies it. `HOT_DEFAULT_CORS_SSE` and `HOT_DEFAULT_CORS_WS` lived in `utils.js`, which `hot.js` then imported the SSE one from purely to re-export it — it never used it. `resolveCors` is called inside each server, so the default each one starts from belongs there too, with the comment explaining why they differ. `pathMatch` moves to `utils.js`. `middleware.js` was reaching through `hot.js` for a url helper, which is the wrong direction: the middleware does not otherwise depend on the hot module, and every other request helper it uses is already in `utils.js`. `hot.js` did not use `pathMatch` itself either — that import was another re-export. And a comment that the lazy-loading change had stranded: "what a transport has to do for itself" describes `CLIENT_STREAM_METHODS`, and `requireServer` had been inserted between the two. What is left in `hot.js` is one thing: the hot lifecycle and the payloads it publishes — its own defaults, the contract a custom transport has to meet, and `createHot`. Transport mechanics are in `./servers`, request helpers in `./utils`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
alexander-akait
force-pushed
the
feat/hot-token
branch
from
October 2, 2026 00:41
eab2f01 to
53cfd98
Compare
alexander-akait
force-pushed
the
refactor/hot-transport-files
branch
from
October 2, 2026 00:41
323fed6 to
1972ffc
Compare
Measuring a build is the server's call rather than the middleware's, and
`hot.progress` made it the middleware's: it applied `ProgressPlugin` to
your compiler, so a server that applies one itself — webpack-dev-server
does — ends up with two of them on one compiler.
What only the middleware can do is carry the result, and the bundled
client already renders `{ action: "progress" }`. So the option becomes one
public method: `instance.publish(payload)` puts a payload of your own on
the stream, and a server hands over what its own plugin reports. It is a
no-op when `hot` is off, so a caller does not have to ask first, and
nothing is sent when no client is connected — a `ProgressPlugin` tick
fires far more often than anyone is listening.
`hot.progress` keeps working until the next major release. Its browser
end, `hot.client.progress`, is unaffected and stays: a server publishing
its own progress payload gets it drawn with no further configuration.
Two things the option did for you become the caller's: rounding the
percent, and dropping a tick that rounds to the same whole number as the
last one.
Also fixes two README links that pointed at headings which do not exist,
for `etag` and for the `attach` method.
Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.
What
hot.jshad one transport in a file of its own and the other inline, which made the structure read as if Server-Sent Events were the special case rather than the default. Both are files now, andhot.jsis the lifecycle and the payloads.hot.jsdrops from 231 lines of stream plumbing to the part that is actually about hot: the webpack hooks,normalizeStats, thebuilt/sync/buildingpayloads, andcreateHot.Each transport owns what is specific to it
The CORS default differed per transport and was decided in
hot.js, away from the code that applies it. Now each file holds its own:What stays in
hot.jsis the mint that hands one token to whichever transport was built — that is genuinely shared.pathMatchmoves toutils.js:middleware.jswas reaching throughhot.jsto get at it, which is not a hot concern.Lazily required, both of them
#2450 made the WebSocket server lazy. This finishes the thought: a project on
'ws'no longer parses the SSE server either, and a custom transport parses neither.Notes for review
types/hot.d.tsloses the declarations that moved, andtypes/servers/EventSourceServer.d.tsis generated — regenerated withnpm run build:types, not written by hand.pathMatchis imported from../src/utils, and the custom-transport assertion now expects the options object the transport is handed rather than one assembled inhot.js.Verified
npm run lint— clean (eslint, prettier, cspell,tsc, client types, schema-check)Stacked on #2451 — review that one first; this PR's diff is only the refactor.
🤖 Generated with Claude Code
https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
Generated by Claude Code