feat: add browser client runtime for HMR#2323
Conversation
Ports the browser client into `client-src/`, mirroring the layout used
in `webpack-dev-server` (source in `client-src/`, built to `client/`).
The client connects to the SSE endpoint via `EventSource`, parses
query-string options from `__resourceQuery`, dispatches `building`,
`built` and `sync` payloads, applies HMR through `process-update.js`
and renders compile-time errors and warnings through an in-page overlay
(`overlay.js`).
Exposed via the `./client` subpath export so users can wire it as a
webpack entry: `require('webpack-dev-middleware/client')`. The source
is transpiled with a browser-targeted babel override and the resulting
files are shipped under `/client`.
Covers the public client API and key SSE handling paths in jsdom: EventSource connection on default and custom paths, ignored heartbeat messages, dispatch of building/built/sync to subscribers, custom handler for unknown actions, warnings on invalid JSON, EventSource wrapper caching across multiple entries, and timeout-driven reconnect.
Adds tests covering the original webpack-hot-middleware client suite (processUpdate invocations on built/sync, errored/warning behavior, overlay show/hide transitions, the overlayWarnings option, the name filter), while keeping the new coverage for heartbeat handling, invalid JSON warnings, EventSource wrapper caching across entries and timeout-driven reconnects.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## hot-middleware #2323 +/- ##
===============================================
Coverage 92.70% 92.70%
===============================================
Files 3 3
Lines 1001 1001
Branches 311 311
===============================================
Hits 928 928
Misses 65 65
Partials 8 8 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
The ported logic called `module.hot.check`/`apply` with both a callback and a Promise-handling branch to support webpack < 2. In webpack 5 both paths fire, so the callback ran twice, triggering a redundant `module.hot.apply` on every update. Drop the legacy callback path and use the Promise API exclusively, which is the canonical webpack 5 contract and matches our peer dependency.
Mirrors the layout webpack-dev-server uses: a separate `tsconfig.client.json` (`noEmit`, browser-targeted libs, `webpack/module` augmentation) runs over `client-src/` via a new `lint:types-client` script, with a small `client-src/globals.d.ts` declaring `ansi-html-community` and the per-page singletons the client stores on `window`. Refines the JSDoc annotations in `client-src/index.js` and `client-src/process-update.js` so `module.hot`, `window` extensions and the HMR `ApplyOptions` type-check cleanly.
Adds a 'Hot Module Replacement client' section explaining how to wire `webpack-dev-middleware/client` as a webpack entry, the query-string options that the runtime understands, and the programmatic `subscribe` / `subscribeAll` / `useCustomOverlay` / `setOptionsAndConnect` exports.
Switches the client-src lint config to the dedicated preset `eslint-config-webpack` ships for browser-targeted CommonJS code, the same family of preset webpack-dev-server uses for its own client. Brings the per-directory rule overrides down to two: `no-console` (legitimately used for HMR status messages) and `no-use-before-define` (relaxed for hoisted function declarations). Adjusts the source to satisfy the rest of the preset directly: adds `use strict` headers, fills in JSDoc for every function, renames `EventSourceWrapper` to `createEventSourceWrapper` (per `new-cap`), names the anonymous module exports, and reorders `performReload` before `handleError` so it is declared before use.
Wraps `webpack/lib/logging/runtime` in a small `utils/log.js` module that exposes a level-based logger registered under the `webpack-dev-middleware` name (matching the infrastructure logger the server side already uses). Replaces every `console.log`/ `console.warn` call in the client and HMR update path with the equivalent `log.info`/`log.warn`/`log.error` calls so output is prefixed and gated by a single `logging` level. User-facing API: - `logging` query-string option accepts `none|error|warn|info|log|verbose` - The previous `log`, `warn`, `noInfo` and `quiet` flags are dropped in favour of `logging` Other cleanups enabled by this: - Drop the `no-console: off` exception from the client-src ESLint config - Update README's client option table accordingly - Add tests covering the new `logging` levels and the logger prefix
Replaces regex-based `some(([msg]) => /.../).toBe(true)` checks on the mocked console with `toMatchSnapshot()` over `mock.calls`. The snapshots capture the exact log lines including the `[webpack-dev-middleware]` prefix and per-call argument count, so any change to the log format surfaces in the test output instead of silently passing. Adds explicit assertions to the existing error / warning flow tests so `console.error` / `console.warn` mocks are not only silenced but also verified to be called with the expected output.
`eslint-config-webpack@4.9.6` still ships `browser-outdated-recommended-commonjs` with `configs["javascript/es5"]` and no parser override, so `const` is rejected. The module variant of the same preset patches this upstream — we replicate the patch locally until the commonjs variant does the same.
* feat: add browser client runtime for HMR
Ports the browser client into `client-src/`, mirroring the layout used
in `webpack-dev-server` (source in `client-src/`, built to `client/`).
The client connects to the SSE endpoint via `EventSource`, parses
query-string options from `__resourceQuery`, dispatches `building`,
`built` and `sync` payloads, applies HMR through `process-update.js`
and renders compile-time errors and warnings through an in-page overlay
(`overlay.js`).
Exposed via the `./client` subpath export so users can wire it as a
webpack entry: `require('webpack-dev-middleware/client')`. The source
is transpiled with a browser-targeted babel override and the resulting
files are shipped under `/client`.
* test: add browser client runtime tests
Covers the public client API and key SSE handling paths in jsdom:
EventSource connection on default and custom paths, ignored heartbeat
messages, dispatch of building/built/sync to subscribers, custom
handler for unknown actions, warnings on invalid JSON, EventSource
wrapper caching across multiple entries, and timeout-driven reconnect.
* test: expand browser client coverage to mirror webpack-hot-middleware
Adds tests covering the original webpack-hot-middleware client suite
(processUpdate invocations on built/sync, errored/warning behavior,
overlay show/hide transitions, the overlayWarnings option, the name
filter), while keeping the new coverage for heartbeat handling,
invalid JSON warnings, EventSource wrapper caching across entries and
timeout-driven reconnects.
* ci: run on push and PRs against the hot-middleware umbrella branch
* refactor(client): switch process-update to promise-only HMR API
The ported logic called `module.hot.check`/`apply` with both a callback
and a Promise-handling branch to support webpack < 2. In webpack 5 both
paths fire, so the callback ran twice, triggering a redundant
`module.hot.apply` on every update.
Drop the legacy callback path and use the Promise API exclusively, which
is the canonical webpack 5 contract and matches our peer dependency.
* chore(client): type-check client-src with a dedicated tsconfig
Mirrors the layout webpack-dev-server uses: a separate
`tsconfig.client.json` (`noEmit`, browser-targeted libs,
`webpack/module` augmentation) runs over `client-src/` via a new
`lint:types-client` script, with a small `client-src/globals.d.ts`
declaring `ansi-html-community` and the per-page singletons the client
stores on `window`.
Refines the JSDoc annotations in `client-src/index.js` and
`client-src/process-update.js` so `module.hot`, `window` extensions
and the HMR `ApplyOptions` type-check cleanly.
* docs: document the browser client runtime in README
Adds a 'Hot Module Replacement client' section explaining how to wire
`webpack-dev-middleware/client` as a webpack entry, the query-string
options that the runtime understands, and the programmatic
`subscribe` / `subscribeAll` / `useCustomOverlay` /
`setOptionsAndConnect` exports.
* chore(client): adopt browser-outdated-recommended-commonjs eslint preset
Switches the client-src lint config to the dedicated preset
`eslint-config-webpack` ships for browser-targeted CommonJS code, the
same family of preset webpack-dev-server uses for its own client.
Brings the per-directory rule overrides down to two:
`no-console` (legitimately used for HMR status messages) and
`no-use-before-define` (relaxed for hoisted function declarations).
Adjusts the source to satisfy the rest of the preset directly: adds
`use strict` headers, fills in JSDoc for every function, renames
`EventSourceWrapper` to `createEventSourceWrapper` (per `new-cap`),
names the anonymous module exports, and reorders `performReload`
before `handleError` so it is declared before use.
* refactor(client): route logging through webpack's runtime logger
Wraps `webpack/lib/logging/runtime` in a small `utils/log.js` module
that exposes a level-based logger registered under the
`webpack-dev-middleware` name (matching the infrastructure logger the
server side already uses). Replaces every `console.log`/
`console.warn` call in the client and HMR update path with the
equivalent `log.info`/`log.warn`/`log.error` calls so output is
prefixed and gated by a single `logging` level.
User-facing API:
- `logging` query-string option accepts `none|error|warn|info|log|verbose`
- The previous `log`, `warn`, `noInfo` and `quiet` flags are dropped
in favour of `logging`
Other cleanups enabled by this:
- Drop the `no-console: off` exception from the client-src ESLint config
- Update README's client option table accordingly
- Add tests covering the new `logging` levels and the logger prefix
* test(client): use snapshots for logger output assertions
Replaces regex-based `some(([msg]) => /.../).toBe(true)` checks on the
mocked console with `toMatchSnapshot()` over `mock.calls`. The
snapshots capture the exact log lines including the
`[webpack-dev-middleware]` prefix and per-call argument count, so any
change to the log format surfaces in the test output instead of silently
passing.
Adds explicit assertions to the existing error / warning flow tests so
`console.error` / `console.warn` mocks are not only silenced but also
verified to be called with the expected output.
* chore: document why client-src needs the ecmaVersion override
`eslint-config-webpack@4.9.6` still ships `browser-outdated-recommended-commonjs`
with `configs["javascript/es5"]` and no parser override, so `const`
is rejected. The module variant of the same preset patches this
upstream — we replicate the patch locally until the commonjs variant
does the same.
* refactor(client): migrate to ES modules and update Babel configuration
* fixup!
|
@bjohansebas Please don't merge such things in future without approving, at least 1 approve, I don't make it strict to be more flexibility, but it doesn't mean we should ignore it, I don't merge then because we need to change to architecture problems on webpack and dev server side |
|
this isn't merged into |
|
Yeah, I see it, just for future, with other branches will be good to make the right architecture too, but it was already merged, so we will work with branch together, I don't like it because it makes a big diff and often unreadable |
Summary
What kind of change does this PR introduce?
Did you add tests for your changes?
Does this PR introduce a breaking change?
If relevant, what needs to be documented once your changes are merged or what have you already documented?
Use of AI