Skip to content

Advisory diagnostic: explain why a page/layout ships (names the elision blocker) #646

@vivek7405

Description

@vivek7405

Problem

#621 documented the "keep pages/layouts as pure carriers" pattern so a page/layout's module stays out of the browser (import-only #605 / inert #179). But the failure is still INVISIBLE until someone inspects the network tab: when a page/layout ships its own module, nothing tells the author WHY. The cause is always a specific client-effecting NON-component in the route's closure (a util that touches a client global, a module-scope side effect, a bare side-effect import), or the route module's own client work.

A diagnostic that NAMES the blocker turns an invisible regression into an actionable line. This was the optional/stretch half of #621, deferred so the guidance could ship on its own.

Design / approach

An ADVISORY (not a hard failure) that, for each route module the build does NOT drop, reports the first client-effecting member of its closure (or its own flagged signal). Reuse the existing analyzeElision machinery (it already computes inertRouteModules / importOnlyRouteModules and the reactiveFiles / clientRouterFiles / clientGlobalOrBareFiles sets in packages/server/src/component-elision.js); the diagnostic is a reporting layer over the verdict, not new analysis.

Surface options (pick one or more): a webjs doctor check, a webjs check ADVISORY rule (must be advisory, not a hard error: a page legitimately MAY need to ship, the analyser biases toward shipping by design, server AGENTS invariant 7), or a @webjsdev/mcp introspection field. Example output: app/page.ts ships whole: lib/utils/cn.ts references a client global and is not a component (move the client part out, or it stays in the boot).

Implementation notes (for the implementing agent)

  • Where: packages/server/src/component-elision.js already exposes the verdict via analyzeElision; to name the blocker, expose (or recompute) the first isClientEffecting non-component in a SHIP route module's closure. The webjs doctor path (packages/cli) or webjs check (packages/server/src/check.js) is the host; the MCP (packages/mcp/src/*) could project it too.
  • MUST be advisory: do NOT make it a hard webjs check correctness rule. A page that genuinely ships (a real module-scope side effect, a client-router import, a non-component the author intends to load) is valid; this is a "you may not have intended this" hint. Respect server AGENTS invariant 7 (elision is conservative, biased to ship).
  • Reuse the build's verdict so it only fires on modules that genuinely ship (the same posture as no-server-import-in-browser-module).
  • Tests: a fixture route pinned by a client-effecting non-component reports the blocker by name; a legitimately-shipping route is not falsely alarmed; an import-only / inert route reports nothing. Counterfactual where applicable.
  • Docs: note the diagnostic in agent-docs/components.md (the carrier-hygiene section dogfood: guide AI agents to keep pages/layouts out of the browser network #621 added) + the relevant CLI doc.

Acceptance criteria

  • A route module that ships gets an advisory naming the first client-effecting non-component (or its own flagged signal) in its closure
  • Advisory-only: it never fails the build / webjs check, and never fires on an import-only or inert route
  • Built over the existing analyzeElision verdict (no parallel re-analysis)
  • Tests cover: pinned-by-non-component (named), legitimately-shipping (not alarmed), import-only/inert (silent); a counterfactual where applicable
  • Docs note the diagnostic and how to act on it

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type
No fields configured for issues without a type.

Projects

Status
Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions