Skip to content

feat: performance tracks on by default under vite dev (performanceTracks option) - #379

Merged
ryansolid merged 1 commit into
nextfrom
perf-tracks-dev-default
Sep 27, 2026
Merged

ryansolid merged 1 commit into
nextfrom
perf-tracks-dev-default

Conversation

@ryansolid

Copy link
Copy Markdown
Member

What

Chrome Performance panel tracks are on by default under vite dev. A new performanceTracks option controls it; the plugin injects a small virtual module, virtual:solid-performance-tracks, that does

import { enablePerformanceTracks } from '@solidjs/web/performance-tracks';
enablePerformanceTracks(<serialized options>);

and reaches the page ahead of the app's entry, so hydration and the first interaction are on the timeline without the app calling enablePerformanceTracks() itself.

Why

@solidjs/web/performance-tracks (rc.10) paints Solid's records — re-runs, interactions, holds, async flights, navigations, server-function calls, and the server's own Server-Timing spans read back — as custom tracks in the Performance panel. Today every app has to wire the enable call into its own entry, before rendering, and remember to strip it for production. The plugin already owns the client entry in start mode and the dev-serve lifecycle everywhere, so it can do that once, correctly, for every Solid 2 app — the same way the diagnostics bridge is wired.

Semantics

performanceTracks vite dev (serve) vite build (incl. dev: true, observe: true) / preview / vitest
omitted or true enabled, adapter defaults off
{ minMs, rich, attribution } enabled with those options passed through off
false off off
  • PerformanceTracksOptions is imported type-only from @solidjs/web/performance-tracks. All three fields are plain data (attribution is AttributionOptions — numbers, booleans, string literals, false, plain objects of those; no functions), so the object is JSON.stringify'd into the module.
  • Never on vite build, by decision: tracks are a dev-server feature. An observe build that wants tracks in production calls enablePerformanceTracks() itself.
  • Test mode (mode === 'test', vitest) and preview are excluded, mirroring the diagnostics surface's apply.
  • Guard: at configResolved the plugin reads the app's @solidjs/web package.json exports and, if ./performance-tracks is missing (unreachable within the ^2.0.0-rc.10 peer range), warns once and skips the injection instead of leaving an unresolvable import in the entry.
  • Server side needs nothing: @solidjs/web's server.ts appends the trace's Server-Timing metrics (solid-shell, solid-boundary, solid-invocation) at head commit in dev builds; the adapter reads them back onto the Server track. Noted in the module comment.
  • @solidjs/web/performance-tracks was already in optimizeDeps.include under serve (fix: rc.10 follow-ups — live server-function address in dev, event-stream preview passthrough, attribution/perf-tracks pre-bundling #374); the virtual module's import resolves through the optimizer, so it shares the app's @solidjs/signals core (asserted).

Start-only vs all apps

All apps, for parity with the diagnostics bridge, which reaches plain index.html apps through transformIndexHtml and start-mode apps through the client entry:

  • Start mode (generated and authored client entries): the import is emitted/prepended as the first source import, ahead of the diagnostics bridge import, the toolbar, the app, and the hydrate()/render() call. (The JSX compiler hoists its own @solidjs/web helper import above everything; evaluating the runtime module renders nothing, so the ordering that matters holds.)
  • Plain apps: a <script type="module" src="/@id/virtual:solid-performance-tracks"> injected with head-prepend — one deliberate deviation from the bridge's head: this module has an ordering requirement and module scripts execute in document order, so prepending guarantees it evaluates before the app's entry script wherever that sits.

Implementation

  • src/performance-tracks/index.ts (new): option resolution, subpath guard, module codegen, the solid:performance-tracks plugin (apply serve-only/non-preview/non-test, resolveId/load for the virtual id with the same bare-import resolution assist as the bridge, transformIndexHtml).
  • src/index.ts: the performanceTracks option + JSDoc, resolvePerformanceTracksOptions, plugin registration, performanceTracks handed to startServe.
  • src/ssr/index.ts: startServe internal performanceTracks flag resolved in configResolved (same gates as the plugin), the import first in both generated entries and prepended in the authored-entry transform.
  • package.json: @solidjs/web added to devDependencies (already a peer) so the type-only import compiles; lockfile moves it from the auto-installed-peer slot.
  • README: #### options.performanceTracks (with the semantics table and a pointer to the Solid 2.0 diagnostics guide's Performance panel section) and a line in the start-mode Dev bullet. Note: diagnostics has no README option section to mirror; this one follows the shape of the surrounding option sections.

Tests (examples/start-ssr)

  • dev: the generated entry imports the module ahead of the toolbar, the app and hydrate(); the served module calls enablePerformanceTracks({}); the module imports the pre-bundled adapter (.vite/deps/@solidjs_web_performance-tracks.js); browser: a PerformanceObserver on measure installed before the first click sees entries with detail.devtools.trackGroup === 'Solid' and a track from the adapter's table (Interactions, Propagation, Effects, …).
  • entries: the authored src/entry-client.tsx is served with the import ahead of its own imports and hydrate().
  • prod / observe: no injection in client assets or the server bundle (markers: the virtual id, track-entry, trackGroup).
  • perf-tracks (new mode): performanceTracks: false → no import and the virtual id doesn't resolve; object form → enablePerformanceTracks({"minMs":2,"rich":false}); mode: 'test' → no import; an index.html app (temp root inside the example) gets the module script in <head> before its entry and false injects nothing; a dev: true build resolves the dev runtime and carries no injection.

Results: start-ssr 626/626 + http-bridge 10/10 + components-warning 11/11 + webworker-warning 12/12; start-client 65/65; start-env 47/47; ssr 12/12 + 8/8; root pnpm test (vite-8, Cypress) passes; pnpm build, pnpm check (100/100) pass.

Public API changes

  • New option performanceTracks?: boolean | PerformanceTracksOptions on the plugin's Options. Default: true — behaviour change under vite dev for every app: the tracks module is injected and enablePerformanceTracks() runs before the app renders (a no-op with dev: false and no observe, where the production runtime is served). false restores the previous behaviour. No change to build output, preview, or test mode.
  • Exported type PerformanceTracksOption = boolean | PerformanceTracksOptions appears in the emitted declarations (the option's type); PerformanceTracksOptions itself is @solidjs/web/performance-tracks' type, not re-exported.
  • No new diagnostic codes; one new startup warning (missing ./performance-tracks subpath), reachable only outside the peer range.

…cks option)

New `performanceTracks` option (`boolean | PerformanceTracksOptions`, default
`true`): under `vite dev` the plugin injects `virtual:solid-performance-tracks`
— a module that calls `@solidjs/web/performance-tracks`' enablePerformanceTracks()
with the serialized options — ahead of the app's entry: a head-prepended module
script for index.html apps (transformIndexHtml), the first import of the
generated or authored client entry in start mode. Hydration and the first
interaction land on the Chrome Performance panel's Solid tracks without the app
enabling them itself. Never on vite build (dev: true and observe builds
included), preview, or test mode; skipped with a warning if the installed
@solidjs/web lacks the ./performance-tracks subpath. The dev server already
writes its Server-Timing metrics, so nothing changes server-side.

Tests (examples/start-ssr): dev asserts the import order in the generated
entry, the served module, the pre-bundled adapter import, and a browser-level
check that the first click paints measure entries with detail.devtools.track;
entries asserts the authored-entry transform; prod/observe assert no injection
in build output; a new perf-tracks mode covers false, the options form, test
mode, an index.html app, and a dev: true build.

Co-authored-by: Cursor <cursoragent@cursor.com>
@changeset-bot

changeset-bot Bot commented Sep 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d6eb3e9

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

This PR includes changesets to release 1 package
Name Type
@solidjs/vite-plugin 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

@pkg-pr-new

pkg-pr-new Bot commented Sep 27, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@solidjs/vite-plugin@379

commit: d6eb3e9

@ryansolid
ryansolid merged commit ac0e413 into next Sep 27, 2026
6 checks passed
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