Skip to content

feat(client): format one of webpack's problems, wherever it is read - #2438

Merged
alexander-akait merged 1 commit into
mainfrom
feat/client-problem-formatter
Sep 30, 2026
Merged

alexander-akait merged 1 commit into
mainfrom
feat/client-problem-formatter

Conversation

@alexander-akait

@alexander-akait alexander-akait commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Stacked on #2437 — that branch is the base, so this shows only the delta.
GitHub retargets it to main when #2437 merges.

Why

webpack-dev-server has no overlay code left after this, which is the point.

The overlay took formatted strings only. The middleware formats its own
payloads on the server, so its client never needed anything else — but a
server that sends webpack's error objects to the browser and formats them
there had to write that formatting itself. That is what webpack-dev-server's
client-src/overlay.js was: 177 lines whose substance was
problemLocation/problemBody/problemLine (webpack's error object → the
overlay's string shape) and formatProblem for its console. An adapter is
still a client file.

What

showProblems takes an error object as it comes:

import { showProblems } from "webpack-dev-middleware/client/overlay";

showProblems("errors", stats.errors, "build");

and the formatting is a module of its own, for a console as much as an overlay:

import { formatProblem } from "webpack-dev-middleware/client/problem";

const { header, body } = formatProblem("error", error);
// { header: "ERROR in ./src/app.js 3:0", body: "Module parse failed: ..." }

problemLocation, problemBody, problemLine and formatProblem, exported
from ./client/problem and re-exported from ./client/overlay so a consumer
doing both has one import.

Two things the server got wrong

src/hot.js builds the same shape now, so a build reads the same way whether
the middleware formatted it or a server sent the object over. A unit test
asserts the two agree across every shape rather than trusting them to.

Falling out of that:

  • A heading with nothing in it. An error webpack names no module for sent
    " \nmessage" — a first line holding a single space — and the overlay reads
    the first line as the heading. The existing test pinned it
    (expect(formatErrors([{ message: "boom" }])).toEqual([" \nboom"])). It is
    reachable: a real build's Module not found: Error: Can't resolve 'x' for an
    entry comes through as { loc: "c", message: … } with no moduleName. The
    message now goes alone.
  • The loader chain instead of the file. A module built by loaders reports
    its whole request as moduleName, which reads as noise where the file is
    what matters. The file comes first now, the request after it, and file is
    used when webpack sets one — once, not appended to itself
    (./a.js (./a.js)), which is what both the naive version and my own first
    attempt produced. test/problem.test.js covers that case because it caught
    me.

I checked what webpack 5 actually sets before writing these rules, rather than
assuming: for a parse error, a missing module, and a loader failure,
moduleName is the bare module (./src/c.js) and file is unset — so the
loader and file branches are rules for input this project does not produce
itself, and matter for a server that sends its own objects.

Proof it does the job

On a dev-server checkout with this packed and installed,
client-src/overlay.js and test/client/ReactErrorBoundary.test.js (the React
error-boundary heuristic is tested here already) delete outright, and
client-src/index.js calls this package directly:

import configureOverlay, {
  clear as clearOverlay,
  formatProblem,
  showProblems,
} from "webpack-dev-middleware/client/overlay";

with the id, the Trusted Types policy name and the open-in-editor route as
configureOverlay options, showProblems/clearOverlay in place of the
send({ type }) state machine — the per-source slots are what that machine
was for — and formatProblem for the console. dev-server's overlay e2e suite:
39 pass. That change goes on webpack/webpack-dev-server#5750.

showProblems("warnings", filtered, source) is also called there without a
length check, which #2437 makes safe.

State

e2e 138 pass across 12 suites, unit 6909 across 17 (test/problem.test.js is
18 of them, formatErrors gained 5), lint / both typecheck passes / spelling /
the precompiled schema check / the full build clean. test/logging.test.js has
the two root-only chmod failures it has on main in this container.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • The error overlay can now display webpack error and warning objects alongside plain-text messages, including module, file, location, message, and stack details.
    • Added formatting utilities for presenting webpack problems outside the overlay.
  • Bug Fixes
    • Improved diagnostic formatting when module or file details are missing, and avoided repeating file information when it matches the module.

@changeset-bot

changeset-bot Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 57f595d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
webpack-dev-middleware Minor

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

alexander-akait added a commit to webpack/webpack-dev-server that referenced this pull request Sep 29, 2026
`client-src/overlay.js` was the last of it: an adapter whose substance was
turning webpack's error objects into the shape the shared overlay renders, and
a `send({ type })` state machine over `showProblems`/`clear`. Both belong
elsewhere. The formatting is webpack-dev-middleware's `client/problem` now,
and the state machine was standing in for per-source slots that overlay
already has, so the events map onto two calls.

What stays is this package's identity on top of a shared overlay, as options:
the `webpack-dev-server-client-overlay` element id, so anything querying it is
unaffected; the `webpack-dev-server#overlay` Trusted Types policy name, which
a page's CSP allowlists by name; and the `/webpack-dev-server/open-editor`
route that makes a file reference clickable.

`test/client/ReactErrorBoundary.test.js` goes with it — the heuristic it
covers lives in webpack-dev-middleware, which tests it.

Requires webpack/webpack-dev-middleware#2438.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
Base automatically changed from fix/overlay-and-indicator-empty-states to main September 30, 2026 09:00
The overlay took formatted strings only. The middleware formats its own
payloads on the server, so its client never needed anything else — but a
server that sends webpack's error objects to the browser and formats them
there had to write that formatting itself, which is what webpack-dev-server
carried a client file for.

`showProblems` now takes an error object as it comes, and the formatting is
`webpack-dev-middleware/client/problem`: `problemLocation`, `problemBody`,
`problemLine` and `formatProblem`, the last splitting a problem into the
header and body a console wants.

The server's own `formatErrors` builds the same shape, so a build reads the
same way whether the middleware formatted it or a server sent the object over.
Two things it got wrong fall out of that:

  * An error webpack names no module for sent a first line holding a single
    space. The overlay reads the first line as the heading, so it drew one
    with nothing in it. The message goes alone now.
  * A module built by loaders reports its whole request as `moduleName`
    (`babel-loader!./app.js`), which reads as noise where the file is what
    matters. The file comes first, the request follows it, and `file` is used
    when webpack sets one — once, not appended to itself.

A unit test asserts the two formatters agree on every shape rather than
trusting them to.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
@alexander-akait
alexander-akait force-pushed the feat/client-problem-formatter branch from 1e47daa to 57f595d Compare September 30, 2026 09:04
@coderabbitai

coderabbitai Bot commented Sep 30, 2026

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 1ed82d43-3f2c-43cc-87a9-09c27054f909

📥 Commits

Reviewing files that changed from the base of the PR and between d54e87d and 57f595d.

📒 Files selected for processing (10)
  • .changeset/feat-problem-formatter.md
  • client-src/overlay.js
  • client-src/problem.js
  • package.json
  • src/hot.js
  • test/e2e/overlay.test.js
  • test/hot.test.js
  • test/problem.test.js
  • types/client/overlay.d.ts
  • types/client/problem.d.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.


Walkthrough

The change adds browser-side formatters for webpack problems and exposes them through the package and overlay declarations. The overlay now accepts strings or problem objects. Server error headings include available module, loader, file, and location details, and omit an empty heading when those details are absent. Tests cover client formatting, overlay rendering, and server output.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 57f59

The change adds structured problem formatting while preserving supported server inputs. No merge-blocking issue remains; merge after normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 57f59

The change primarily broadens diagnostic input and formatting. Structured problems are converted into strings before display, and no new privilege or boundary bypass was demonstrated. Risk remains low rather than minimal because HTML escaping and failure recovery could not be fully verified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated exposure is diagnostic text reaching the browser overlay, plus downstream consumers of the new formatting API. Repository evidence does not establish how external consumers render those strings or what environments expose the overlay.

Trust Boundaries and Controls

  • observed — Structured problem fields are normalized into strings before reaching the overlay rendering path. Inspected rendering code calls encodeHtmlEntity before HTML generation and assignment. Helper correctness and the exact base comparison remain unverified, so this supports continuity of the display boundary without proving complete injection resistance.

Resilience and Maintainability Implications

  • inferred — Available evidence places normalization before per-source storage and preserves empty-input clearing, which argues against partial per-item state mutation during normalization. It does not resolve whether formatter failures, deferred iframe creation, or interleaved calls can leave stale visible diagnostics; no material recovery regression was established.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: client-side formatting of webpack problems across their consumers.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 8 files. (2 skipped: 2 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Sep 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.22%. Comparing base (d54e87d) to head (57f595d).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2438      +/-   ##
==========================================
+ Coverage   96.15%   96.22%   +0.06%     
==========================================
  Files          19       20       +1     
  Lines        2161     2200      +39     
==========================================
+ Hits         2078     2117      +39     
  Misses         83       83              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@alexander-akait
alexander-akait merged commit cc0b244 into main Sep 30, 2026
22 checks passed
@alexander-akait
alexander-akait deleted the feat/client-problem-formatter branch September 30, 2026 10:38
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