Skip to content

feat!: consolidate diagnostic codes; sourceNames follows dev in both compilers - #3646

Merged
ryansolid merged 3 commits into
nextfrom
codes-source-names
Sep 25, 2026
Merged

ryansolid merged 3 commits into
nextfrom
codes-source-names

Conversation

@ryansolid

Copy link
Copy Markdown
Member

Fourth PR of the public-API consolidation. Two legs.

Leg A — diagnostic codes

Codes are public API (Sentry fingerprint roots). Three consolidations, zero stale references outside CHANGELOG.md and the historical .changeset/*.md entries.

Before After Kind Severity data
WIDE_WRITE (engine, wideWrites threshold) + HUGE_FAN_OUT (core, 2000) HUGE_FAN_OUT — one code, one threshold story graph warn { count } from the core; { count, write: "write" | "refresh" | "async" } from the engine
SERVER_FN_ERROR_SANITIZED SERVER_ERROR_SANITIZED ssr error { source: "server-function", error, wire }
SSR_ERROR_SANITIZED SERVER_ERROR_SANITIZED ssr info { source: "ssr", error, wire }
ASYNC_WATERFALL (data.side: "server") SSR_BOUNDARY_WATERFALL ssr info (2 waits) / warn (3+) { boundary, passes, sequentialMs }
  • HUGE_FAN_OUT. The always-on core check (noteFanOut, packages/signals/src/core/dev.ts) is the single emitter; the attribution engine's checkFanOut (attribution.ts, stampWrite funnel) calls it from its lower fanOut threshold (default 250) and hands over at GRAPH_SIZE_WARN_AT (2000) so one change never carries two findings. Both share the graphSizeWarnedAt dedupe (re-warn after +500). One message text; the engine's finding adds data.write. WIDE_WRITE's doubling policy and data.subscribers are gone.
  • SERVER_ERROR_SANITIZED. Emitted by record() in packages/solid/src/server/signals.ts (SSR road, info) and sanitizeServerError in packages/web/server-functions/src/server.ts (server-function road, error). Severity carries the road; data.source names it.
  • SSR_BOUNDARY_WATERFALL. devCheck in packages/solid/src/server/hydration.ts, off the <Loading> boundary record's passes. ASYNC_WATERFALL is the client's graph-proven sequential-flights verdict again, only.

Guide anchors: RFC 08 #### HUGE_FAN_OUT, #### SERVER_ERROR_SANITIZED, #### SSR_BOUNDARY_WATERFALL; packages/solid/skills/reactivity-diagnostics/SKILL.md ### HUGE_FAN_OUT / ### SERVER_ERROR_SANITIZED / ### SSR_BOUNDARY_WATERFALL; packages/diagnostics/skills/agent-loops/SKILL.md updated; RFC 10/12 link #server_error_sanitized.

Leg B — sourceNames parity and dev default

  • Both JSX compilers take sourceNames?: boolean | { components?: boolean; bindings?: boolean } and resolve it identically: unset → every kind is dev; boolean → every kind; object → each unspecified kind is dev. dev is PluginConfig.dev (Babel) / TransformOptions.dev (native, resolved in node_adapter.rs::core_options; the Rust core SourceNames stays explicit, Default all-off).
  • Production output (dev: false) is byte-identical with the option set or unset — asserted in packages/compiler/__tests__/transform.test.js and packages/babel-plugin/test/dom-source-names.spec.js on the same source; the existing prod fixture suites are unchanged.
  • Only the dev fixtures changed: __dom_hydratable_dev_fixtures__/walkValidation (Babel) and __tests__/fixtures/dom-hydratable-dev/walkValidation (native) now carry span.children-style insert names; the parity harness (dom-hydratable-dev mode) passes.
  • No JSX-level gap between the compilers: both implement components (DOM + SSR) and bindings (DOM only) with the same shape.
  • Primitive naming stays with the native standalone transformSourceNames pass, which the build tool runs on every module independently of the JSX compiler (solid-vite-plugin #371, solid: { sourceNames }). No Babel counterpart; READMEs, types.d.ts, config.rs docs and RFC 08 say so.

Public API changes

Diagnostic codes (@solidjs/signals DiagnosticCode; emitters in solid-js server, @solidjs/web/server-functions)

  • Removed: WIDE_WRITE, SERVER_FN_ERROR_SANITIZED, SSR_ERROR_SANITIZED.
  • Added: SERVER_ERROR_SANITIZED (kind ssr; error from the server-function wire, info from the SSR wire; data: { source: "ssr" | "server-function", error, wire }), SSR_BOUNDARY_WATERFALL (kind ssr; info/warn; data: { boundary, passes, sequentialMs }).
  • Changed: HUGE_FAN_OUT now also fires from the attribution engine at fanOut; data.write?: "write" | "refresh" | "async" added; data.count unchanged. ASYNC_WATERFALL no longer has a server variant; data.side is gone.
  • AttributionOptions.wideWrites → AttributionOptions.fanOut (number | false, default 250; checks: false folds it) — packages/signals/src/core/attribution.ts, re-exported through solid-js/attribution.
  • New exported type FanOutWrite in packages/signals/src/core/dev.ts.

Compilers

  • @solidjs/babel-plugin PluginConfig.sourceNames is now optional and defaults to dev (was false); in the object form each unspecified kind defaults to dev (was false). Dev builds now emit createComponent(Comp, props, "Comp") and binding-effect names by default; SSR dev output keeps the createComponent call it previously inlined.
  • @solidjs/compiler TransformOptions.sourceNames defaults to dev (was false); object form as above. SourceNames::all(bool) added to the Rust core.
  • No new option keys; no primitives key on either JSX compiler.

Size

scripts/size gzip, merge-base dcca7d46e → branch: every prod scenario ±0; app: CSR, observe tier 17677 → 17666 (−11); observe tier + attribution engine 31686 → 31607 (−79). All limits pass.

Verification

@solidjs/signals, solid-js, @solidjs/web (client, vite.config.server.mjs, vite.config.hydrate.mjs), @solidjs/diagnostics, @solidjs/babel-plugin (typecheck + build + vitest), @solidjs/compiler (cargo test ×3 feature sets + native debug build + vitest incl. parity) all green; pnpm types and the runtime packages' test-types/typecheck clean.

Rebased onto next at 78523bf (#3640, #3641; clean, no conflicts). Post-rebase: pnpm types, @solidjs/signals, solid-js, @solidjs/web client + vite.config.server.mjs re-run green (after rebuilding packages/web so the frames dist artifacts those upstream tests read are current).

Follow-ups

  • solid-vite-plugin #371 depends on the option shape landed here: the JSX compiler's sourceNames (components/bindings, defaulting to dev) and the native standalone transformSourceNames pass for primitives. It should read solid: { sourceNames } and forward the JSX-level keys to the compiler and the primitive naming to the standalone pass; no primitives key exists on either JSX compiler.
  • Sentry (feat(solid-2): add @sentry/solid-2 — Solid 2 SDK (client + server) getsentry/sentry-javascript#24517) maps diagnostic codes generically (code → fingerprint root), so nothing in the integration itself breaks. Only its brief's code list needs updating: drop WIDE_WRITE, SERVER_FN_ERROR_SANITIZED, SSR_ERROR_SANITIZED; add SERVER_ERROR_SANITIZED, SSR_BOUNDARY_WATERFALL.

@changeset-bot

changeset-bot Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f9ad287

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

This PR includes changesets to release 11 packages
Name Type
@solidjs/signals Patch
solid-js Patch
@solidjs/web Patch
@solidjs/compiler Patch
@solidjs/babel-plugin Patch
test-integration Patch
@solidjs/element Patch
@solidjs/h Patch
@solidjs/html Patch
@solidjs/universal Patch
@solidjs/diagnostics Patch

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

@codspeed

codspeed Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 176 untouched benchmarks
⏩ 3 skipped benchmarks1


Comparing codes-source-names (f9ad287) with next (384a631)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@ryansolid

Copy link
Copy Markdown
Member Author

CodSpeed regressions — cause and fix (6aaa3df)

What regressed. All three regressions are in the SSR bench lane (packages/web/test/server/*.bench.tsx, run via pnpm --filter @solidjs/web run bench:server): search-results: 50 items 1.5 → 1.9 ms (−24%), spread-static-tail: dynhead 6.2 → 6.9 ms (−10.5%), polymorphic-chain: chain-static 20.4 → 21.7 ms (−5.9%); polymorphic-chain: compiled showed +8.3%. The 172 client and signals benches were untouched.

Cause: side (B), the sourceNames default. Vitest runs the vite plugin with command === 'serve', so it compiles the benches with dev: true; with this PR sourceNames follows dev, and the SSR codegen keeps _$createComponent(Comp, props, "Name") instead of inlining Comp(props) (packages/babel-plugin/src/shared/component.ts:368-386, same in the Rust compiler). The runtime is loaded from source, where "_SOLID_DEV_"/"_SOLID_OBSERVE_" are unreplaced (truthy) strings, so every component now goes through observedComponent (packages/solid/src/server/component.ts:96-112): createComponentOwner → allocateOwner (pool pop + 11 field resets), a `<${name}>` string, a closure, and a runWithOwner try/catch/finally — per component instance. I confirmed the only compile-output difference between dev:true and dev:true, sourceNames:false on the bench and harness files is that wrapper; nothing else in the diff reaches these benches (the SSR lane doesn't import @solidjs/signals, so the noteFanOut/checkFanOut consolidation can't be it — and that check only runs with the attribution engine installed anyway).

The magnitudes line up with component count: search-results creates ~52 owners on a ~1.5 ms render (−24%), chain-static ~1000 (−5.9%). spread-static-tail has exactly one component per render, so dynhead −10.5% while tail/head/mixed are untouched — that one, and the compiled +8%, are the "different runtime environments" noise CodSpeed itself flags on the report.

Local reproduction. Wall-clock on my machine was noisy, so I A/B'd the two call shapes in one vitest process, interleaved, on the search-results fixture (hand-written createComponent(Item, {item}, "Item") vs the inlined form): min 0.044 ms → 0.049–0.052 ms, mean 0.052 → 0.058 ms in the quiet round — +11–17% on real hardware vs. CodSpeed's +24% in instruction counts. Direction and order of magnitude match.

Fix. The compilers' default stays as merged (sourceNames follows dev). packages/web/vite.config.server-bench.mjs now pins sourceNames: false explicitly, so the SSR lane keeps measuring the production component shape it measured on next. This is a harness change and it does change what the lane measures relative to a naive dev compile: it measures the dev-tier runtime with prod-shaped component calls, exactly as before this PR — not the labelled-owner cost, which is a diagnostics-tier cost dev SSR users will pay (~10–25% on component-dense renders per the numbers above). The client bench config (vite.config.mjs) is shared with the test suite and its benches were untouched, so it's left alone. No changeset (tooling config only).

Verification. pnpm types, then @solidjs/signals (3688 passed), solid-js (781) and @solidjs/web (961 / 1158 / 248 across the three configs) all green after a rebuild; bench:server runs under both the native and Babel compilers.

Separately: the Testing & Coverage job failed on packages/signals/tests/observe-idle-cost.test.ts (observe 377 ms / prod 291 ms = 1.29 > 1.25 cap). That test's own comment budgets ~15 ms per sample; the CI worker was ~25× slower, i.e. heavily loaded. The observe tier's hot path is unchanged in this PR (scheduler.ts:1278 still gates noteFanOut on GRAPH_SIZE_WARN_AT; only the reporter signature and the engine-side caller changed), and the test passes locally against the rebuilt dist. Looks like a flake of the timing tripwire rather than a regression; a re-run should clear it.

— Claude via Cursor

ryansolid and others added 3 commits September 24, 2026 21:58
…compilers

Diagnostic codes:
- HUGE_FAN_OUT absorbs WIDE_WRITE; the write-side attribution now ships as
  `data.write` on the same code, and the engine emits it directly.
- SERVER_ERROR_SANITIZED replaces SERVER_FN_ERROR_SANITIZED and
  SSR_ERROR_SANITIZED, distinguished by `data.source`.
- SSR_BOUNDARY_WATERFALL split out of ASYNC_WATERFALL (which drops `data.side`).
- AttributionOptions.wideWrites renamed to fanOut (`number | false`, default 250);
  new FanOutWrite type.

Compilers:
- `sourceNames` defaults to `dev` in both @solidjs/babel-plugin and
  @solidjs/compiler; object-form kinds default to `dev`; dev SSR keeps
  `createComponent` naming.
- Primitive naming stays with the native standalone pass; neither JSX compiler
  exposes a `primitives` key.

Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Vitest compiles with `dev: true`, and `sourceNames` now follows `dev` in
both compilers. For SSR output that keeps `createComponent(Comp, props,
"Name")` in place of the inlined `Comp(props)`, so the from-source (dev)
server runtime runs every component under a labelled transparent owner —
`observedComponent`: pool pop + field reset, `<Name>` string, closure,
try/finally per component instance. CodSpeed priced that at +24% on
`search-results: 50 items (renderToString)` and +5.9% on
`polymorphic-chain: chain-static` (~1000 component owners per render);
the remaining two deltas in the report touch benches with a single
component per render and are runtime-environment noise.

That owner is the diagnostics tier's cost, not the SSR runtime cost this
lane tracks. `vite.config.server-bench.mjs` now sets `sourceNames: false`
explicitly so the lane keeps measuring the production component shape it
measured before the default followed `dev`. The compilers' default is
unchanged; the bench lane only measures what it did on `next`.

Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 36097105774

Coverage decreased (-0.05%) to 73.084%

Details

  • Coverage decreased (-0.05%) from the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 1142
Covered Lines: 883
Line Coverage: 77.32%
Relevant Branches: 894
Covered Branches: 605
Branch Coverage: 67.67%
Branches in Coverage %: Yes
Coverage Strength: 25.2 hits per line

💛 - Coveralls

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.

2 participants