Skip to content

fix(compiler): secondary passes accept any file extension - #4320

Merged
ryansolid merged 3 commits into
nextfrom
compiler/secondary-passes-any-extension
Oct 27, 2026
Merged

ryansolid merged 3 commits into
nextfrom
compiler/secondary-passes-any-extension

Conversation

@ryansolid

Copy link
Copy Markdown
Member

Summary

The secondary passes (transformDirectives, lazy, refresh, source names) run on code that is already lowered to JavaScript. Even so, they picked a parser from the filename and failed on any extension other than Solid's own with "Unknown file extension".

That blocks other front ends from server functions. A function ID hashes the filename, and moduleForFunctionId hashes the module's real id. A front end therefore has to pass the real path (App.rx), not a borrowed App.rx.tsx.

With this change a known extension still picks its dialect, and any other extension parses as TSX (config.rs secondary_source_type). The JSX transform is unchanged and still rejects unknown extensions.

This is the companion to solidjs/solid-vite-plugin's frontends option (PR linked there), which ReactiveScript (.rx) uses to build through Solid's Vite plugin.

How did you test this change?

  • New packages/compiler/__tests__/secondary-passes-frontend.test.js:
    • transformDirectives on /project/src/routes/App.rx works in both modes and gives the same ID.
    • That ID differs from the one for App.rx.tsx.
    • The lazy and refresh passes run under that filename.
  • cargo test in packages/compiler: 76 passed.
  • Full vitest: the only failures are 6 parity-probes "exotic literal holes" cases. They fail identically without this change, because my environment's babel-plugin is linked to an older build.
  • End to end: with the plugin branch and this compiler, all 12 ReactiveScript example apps build with Vite 8 and run in Chromium, and the todo app also runs under the dev server.

Generated by Claude Code

transformDirectives, lazy, refresh and source names run on already-lowered
code; the filename is metadata (server-function IDs hash it). An unknown
extension now parses as TSX instead of failing, so a Vite front end can
pass a module's real path (e.g. App.rx).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117BoWMdD4K7882vob4J9vq
@changeset-bot

changeset-bot Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b812bd8

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

This PR includes changesets to release 15 packages
Name Type
@solidjs/compiler Patch
@solidjs/babel-plugin Patch
@solidjs/diagnostics Patch
@solidjs/element Patch
@solidjs/h Patch
@solidjs/html Patch
@solidjs/signals Patch
solid-js Patch
@solidjs/universal Patch
@solidjs/web Patch
test-integration Patch
board-example Patch
canvas-example Patch
server-dbmon-example Patch
todos-server-example 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

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Size (brotli, eager entry chunk)

scenario head vs base minified vs base minified vs recorded cap lazy chunks (not counted)
signals: core floor (createSignal/Memo/Effect/Root/flush) 7.42 KB 0 B 0 B −119 B 7.44 KB ✅
signals: + createStore 14.87 KB 0 B 0 B −1463 B 15.04 KB ✅
signals: + isPending/latest 10.38 KB 0 B 0 B −8 B 10.38 KB ✅
signals: + until/refresh (truth readers, no optimism) 8.05 KB 0 B 0 B −123 B 8.05 KB ✅
app: render + one signal (the simple-app floor) 9.78 KB 0 B 0 B −107 B 9.75 KB ⚠️ over by 29 B, 127 B minified headroom
app: hydrating (no stores) with Show/For/Loading/Errored/lazy 18.60 KB 0 B 0 B −194 B 18.49 KB ⚠️ over by 110 B, 214 B minified headroom lazy-page.js 0.04 KB
app: hydrating + every store primitive family 31.04 KB 0 B 0 B +16 B 30.86 KB ⚠️ over by 182 B, 4 B minified headroom lazy-page.js 0.04 KB
app: CSR with Show/For/Loading/Errored/lazy 13.39 KB 0 B 0 B −186 B 13.40 KB ✅ lazy-page.js 0.04 KB
app: CSR, observe tier (same app on the observe artifacts) 15.10 KB 0 B 0 B −189 B 15.14 KB ✅ lazy-page.js 0.04 KB
app: CSR, observe tier + attribution engine enabled 29.69 KB 0 B 0 B −190 B 29.70 KB ✅ lazy-page.js 0.04 KB
app: compiled floor (one template, one text hole, one delegated click) 9.96 KB 0 B 0 B −106 B 9.93 KB ⚠️ over by 26 B, 126 B minified headroom
app: compiled CSR (JSX todo app: spread/merge/omit, events, class/style, keyed For, Show, Loading + lazy, store) 26.14 KB 0 B 0 B −736 B 26.27 KB ✅ stats.js 0.18 KB
app: compiled hydrating (the same JSX todo app through hydrate(), compiled hydratable) 32.24 KB 0 B 0 B −691 B 32.25 KB ✅ stats.js 0.20 KB
frames: eager client consumer (frames client + transport, lazy codec) 11.75 KB 0 B 0 B 0 B 11.76 KB ✅
page: base server components (hydrating + dynamic + frames + sf reference) 33.49 KB 0 B 0 B −188 B 33.37 KB ⚠️ over by 121 B, 208 B minified headroom assets.js 0.78 KB, decode.js 7.09 KB, lazy-page.js 0.04 KB, regions.js 1.06 KB, trace.js 8.92 KB, wire.js 3.47 KB
page: live server components (base + live/GET + action + isPending/latest) 37.97 KB 0 B 0 B +3 B 37.75 KB ⚠️ over by 216 B, 17 B minified headroom assets.js 0.78 KB, decode.js 7.09 KB, lazy-page.js 0.04 KB, regions.js 1.06 KB, trace.js 8.93 KB, wire.js 3.47 KB
page: compiled base server components (the base page as JSX: templates with class/style/attributes/events, For/Show; no spread) 35.49 KB 0 B 0 B −185 B 35.43 KB ⚠️ over by 65 B, 205 B minified headroom assets.js 0.78 KB, decode.js 7.09 KB, regions.js 1.06 KB, sc-comments.js 0.20 KB, trace.js 8.94 KB, wire.js 3.47 KB
page: compiled live server components (the compiled base page + live/GET + action + isPending/latest) 41.81 KB 0 B 0 B −10 B 41.56 KB ⚠️ over by 255 B, 30 B minified headroom eager (counted): web.js 22.37 KB; assets.js 0.78 KB, decode.js 7.09 KB, regions.js 1.06 KB, sc-comments.js 0.19 KB, trace.js 8.94 KB, wire.js 3.47 KB
page: base + router (base page + @solidjs/router: createRouter, two routes, preload, useNavigate) 41.03 KB 0 B 0 B −189 B 40.81 KB ⚠️ over by 216 B, 209 B minified headroom assets.js 0.78 KB, decode.js 7.09 KB, lazy-page.js 0.04 KB, regions.js 1.06 KB, server.js 1.03 KB, serverForms.js 3.58 KB, trace.js 8.97 KB, wire.js 3.48 KB
page: live + router (live page + @solidjs/router: createRouter, two routes, preload, useNavigate) 47.35 KB 0 B 0 B −4 B 47.16 KB ⚠️ over by 190 B, 24 B minified headroom eager (counted): client.js 27.45 KB; assets.js 0.78 KB, decode.js 7.09 KB, lazy-page.js 0.04 KB, regions.js 1.07 KB, server.js 1.03 KB, serverForms.js 3.32 KB, trace.js 8.95 KB, wire.js 3.49 KB
server: floor (getRequestEvent + isServer) 1.27 KB 0 B 0 B −69 B 1.34 KB ✅
server: renderToString (the server-render floor) 22.79 KB 0 B 0 B 0 B 22.80 KB ✅

⚠️ Over the brotli cap within the minified allowance (passes)

  • app: render + one signal (the simple-app floor): over brotli cap by 29 B; minified 26,922 B vs 27,029 B recorded with the cap (−107 B) — 127 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • app: hydrating (no stores) with Show/For/Loading/Errored/lazy: over brotli cap by 110 B; minified 53,426 B vs 53,620 B recorded with the cap (−194 B) — 214 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • app: hydrating + every store primitive family: over brotli cap by 182 B; minified 95,129 B vs 95,113 B recorded with the cap (+16 B) — 4 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • app: compiled floor (one template, one text hole, one delegated click): over brotli cap by 26 B; minified 27,454 B vs 27,560 B recorded with the cap (−106 B) — 126 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • page: base server components (hydrating + dynamic + frames + sf reference): over brotli cap by 121 B; minified 101,371 B vs 101,559 B recorded with the cap (−188 B) — 208 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • page: live server components (base + live/GET + action + isPending/latest): over brotli cap by 216 B; minified 115,220 B vs 115,217 B recorded with the cap (+3 B) — 17 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • page: compiled base server components (the base page as JSX: templates with class/style/attributes/events, For/Show; no spread): over brotli cap by 65 B; minified 107,733 B vs 107,918 B recorded with the cap (−185 B) — 205 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • page: compiled live server components (the compiled base page + live/GET + action + isPending/latest): over brotli cap by 255 B; minified 123,206 B vs 123,216 B recorded with the cap (−10 B) — 30 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • page: base + router (base page + @solidjs/router: createRouter, two routes, preload, useNavigate): over brotli cap by 216 B; minified 125,198 B vs 125,387 B recorded with the cap (−189 B) — 209 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • page: live + router (live page + @solidjs/router: createRouter, two routes, preload, useNavigate): over brotli cap by 190 B; minified 140,476 B vs 140,480 B recorded with the cap (−4 B) — 24 B of the 20 B minified allowance left; +0 B minified over this PR's base

Bundled with Rolldown (what Vite ships), brotli q11, decimal KB. A scenario fails only when it is over its brotli cap and its minified size is more than 20 B over the minified recorded with the cap; over the cap within that allowance is brotli layout noise and passes with a warning. Caps and their recorded minified in scripts/size/scenarios.js; the floor and page caps in floor-caps.json are frozen (lower only, or Size-Exception: in the PR body). npm run ratchet lowers caps per RC; it never raises one (scripts/size/README.md).

Copy link
Copy Markdown
Member Author

The red Compiler binaries jobs are not caused by this PR. Both build wasm32-wasip1-threads and build aarch64-unknown-linux-gnu fail only in __tests__/parity-probes.test.js, on the "exotic literal holes" probe (c={-1} d={NaN}) in the tsrx-ssr, tsrx-universal and related variants. On the babel side, -1 and NaN are no longer folded into the static template, and d prints as 0 / 0.

The cause is #4309 (55c7329, Babel 8 support). It replaced t.numericLiteral(evaluated) with t.valueToNode(evaluated) in evaluateAndInline and transformAttributes. valueToNode(-1) returns a UnaryExpression and valueToNode(NaN) returns 0 / 0, so the plugin now treats those values as dynamic, while oxc still inlines them. #4309 only touched packages/babel-plugin, so this workflow (paths packages/compiler/**) didn't run on it. This PR is the first compiler-path run since. The same probe fails locally on next without this change. This PR's diff only touches the secondary passes (directives, lazy, refresh, source names), not the JSX transform these probes compare.

No fix exists yet. A proposed fix belongs in the babel plugin, not here: keep numericLiteral for finite non-negative numbers, and have the static checks treat an evaluated number as static whatever node valueToNode builds, for example by inlining String(evaluated) for non-prop attributes as strings already are. I'll leave it to whoever owns #4309, rather than widen this PR.


Generated by Claude Code

@coveralls

coveralls commented Oct 27, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 40534821727

Coverage remained the same at 78.627%

Details

  • Coverage remained the same as 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: 1353
Covered Lines: 1123
Line Coverage: 83.0%
Relevant Branches: 1080
Covered Branches: 790
Branch Coverage: 73.15%
Branches in Coverage %: Yes
Coverage Strength: 28.09 hits per line

💛 - Coveralls

#4309 switched evaluateAndInline to t.valueToNode for numbers, which builds
-1 / 0/0 expressions; the SSR and universal transforms then treated them
as dynamic, diverging from the oxc compiler (parity-probes "exotic literal
holes"). Build NumericLiteral nodes directly where @babel/types 8 rejects
the value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117BoWMdD4K7882vob4J9vq
@codspeed

codspeed Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Merging this PR will degrade performance by 5.49%

2 warnings

⚠️ 2 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

⚠️ Base and head ran on different runner types

Some benchmarks with significant performance changes were compared across different runner types, so the changes might not come from the code. Run base and head on the same runner type, or use CodSpeed Macro Runners for more stable results.

Learn more about Macro Runners

Open the report in CodSpeed to investigate

❌ 1 regressed benchmark
✅ 195 untouched benchmarks
⏩ 5 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
❌ memo + sync render effect only (reference) 29.7 ms 31.4 ms -5.49%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing compiler/secondary-passes-any-extension (b812bd8) with next (0090fe3)2

Open in CodSpeed

Footnotes

  1. 5 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. ↩

  2. No successful run was found on next (8cff139) during the generation of this report, so 0090fe3 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

Copy link
Copy Markdown
Member Author

CodSpeed Performance Analysis on 468423b reports one regression: memo + sync render effect only (reference) in packages/signals/tests/mount-rows.bench.ts, at −6.41%. This PR doesn't cause it. The diff only touches packages/compiler (the Rust secondary passes) and now the babel-plugin literal fix, and neither runs in a signals benchmark. CodSpeed also warns that base and head ran on different runner types. The other 195 benchmarks are unchanged. Nothing to fix here; a maintainer can acknowledge it on CodSpeed. I can't re-run that check from here.


Generated by Claude Code

…sses-any-extension

# Conflicts:
#	packages/babel-plugin/src/shared/utils.ts
@ryansolid
ryansolid merged commit 0c6db49 into next Oct 27, 2026
15 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.

3 participants