feat(compiler,babel-plugin,solid): componentNames for SSR output; ssrScope swaps the id-bearing owner - #3441
Conversation
…Scope swaps the id-bearing owner The SSR generate honours `componentNames` the way `dom` does. Under the option both compilers keep the `createComponent` call they otherwise inline to `Comp(props)` and pass the source tag text — `_$createComponent(Comp, props, "Comp")` — so the server runtime's observe/dev `createComponent` (a transparent, labelled owner around the body, #3433) labels every compiled component and a server finding's `ownerPath` reads `<App> › <Page>` like the client's. Without the option SSR still inlines: prod output is byte-identical. Universal and dynamic stay unlabelled. `@solidjs/vite-plugin` already passes the option for its dev and observe postures for every generate, so app server builds pick this up with no config change. Oxc's `jsx_tag_name` is now a shared, closure-based helper so the SSR lowering reuses it (`this.Row` through the `_self$` pre-pass, member tags). The `ssrInert` fixture becomes `ssr`, widened to the DOM fixture's shapes; the Oxc suite pins that SSR without the option imports no `createComponent`. Compiling the web server suite with the option (vite.config.server.mjs — the dev posture) surfaced a latent `ssrScope` bug: the virtual hole scope swapped the CURRENT owner's `id`/`_childCount`, but content inside a hole resolves ids by walking up past transparent owners, so with one in between the reserved slot was invisible and the hole's content took fresh ids from the enclosing counter (`_hk=3` where the client expects `_hk=10`). Reachable in prod today through `runInServerComponentScope` (transparent), ubiquitous once every component body runs under a labelled owner. The scope now swaps the nearest id-bearing owner (solid/test/server/ssr-scope.spec.ts covers a plain transparent owner, the server-component scope, the labelled component owner, and nesting). server-diagnostics.spec.tsx drops its `mount()` workaround for plain JSX; boundaries are compiled components too, so a boundary's finding now locates as `<App> › <Errored>` / `<App> › <Loading>` — as on the client. Server, hydrate and client suites pass under the native compiler and JSX_COMPILER=babel. Co-authored-by: Cursor <cursoragent@cursor.com>
🦋 Changeset detectedLatest commit: a36b9f6 The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
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 |
Coverage Report for CI Build 34903669436Coverage remained the same at 71.842%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Merging this PR will degrade performance by 10.43%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | projection derive: write one NESTED field (reference) |
2.5 ms | 2.8 ms | -10.43% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing ssr-component-names (a36b9f6) with next (c452850)
Follow-up to #3433, closing the "known gap" it left: compiled SSR JSX now gets the component label, so a server finding's
ownerPathreads<App> › <Page>like the client's.Compilers
componentNamesapplies to thessrgenerate the way it does todom. Under the option both compilers keep thecreateComponentcall they otherwise inline toComp(props)and pass the source tag text:Without the option SSR still inlines — prod output is byte-identical. Universal and dynamic stay unlabelled (user-owned two-argument
createComponent).@solidjs/vite-pluginalready passes the option for its dev and observe postures for every generate, so app server builds (Solid Start, TanStack Start) pick this up with no config change.shared/component.ts— the label gate isdom || ssr; the SSR inline is skipped when labelled.AstSsrTransformgainscomponent_names/uses_create_component;jsx_tag_namebecomes a shared closure-based helper so the SSR lowering reuses the samethis-via-_self$and member-tag handling as DOM.__dom_component_names_fixtures__/ssrInert→ssr, widened to the DOM fixture's shapes; the Oxc suite additionally pins that SSR without the option imports nocreateComponent. Parity harness green.Runtime:
ssrScopeunder transparent ownersCompiling the web server suite with the option (
vite.config.server.mjs, i.e. the dev posture) surfaced a latent bug. The virtual hole scope swapped the current owner'sid/_childCount, but content inside a hole resolves its ids by walking up past transparent owners — so with a transparent owner between the hole and its id-bearing ancestor, the reserved slot was invisible and the hole's content took fresh ids from the enclosing counter (_hk=3where the client expects_hk=10).This is reachable in prod today through
runInServerComponentScope(its owner is transparent); with labels it would have been every component body. The scope now swaps the nearest id-bearing owner.solid/test/server/ssr-scope.spec.tscovers a plain transparent owner, the server-component scope, the labelled component owner, and nesting — all four failed before the fix.Tests
server-diagnostics.spec.tsxdrops itsmount()workaround for plain JSX. Boundaries are compiled components too, so a boundary's finding now locates as["<App>", "<Errored>"]/["<App>", "<Loading>"]— matching the client.JSX_COMPILER=babel; compiler vitest 5779 + cargo; babel-plugin 257; solid 618.Out of repo
vite-plugin-solid's comment nearcomponentNamesstill says "DOM-only by construction (the ssr generate ignores the flag)". Behavior there is already correct; only the comment is stale.