Skip to content

[PoC] Rewrite nested JSX component paths to direct hoisted exports#8293

Open
fhammerschmidt wants to merge 6 commits intorescript-lang:masterfrom
fhammerschmidt:poc-jsx-component-nested-exports
Open

[PoC] Rewrite nested JSX component paths to direct hoisted exports#8293
fhammerschmidt wants to merge 6 commits intorescript-lang:masterfrom
fhammerschmidt:poc-jsx-component-nested-exports

Conversation

@fhammerschmidt
Copy link
Member

@fhammerschmidt fhammerschmidt commented Mar 12, 2026

ReScript currently lowers nested JSX component paths like <Sidebar.Provider> to cross-module member access in generated JS, for example Sidebar.Provider.make. That works in many cases, but it breaks in environments like Next.js Server Components, where nested component member access can end up as undefined at runtime even though direct top-level component bindings work.

This PR changes JSX v4 lowering so nested component modules also emit a hidden direct export for their underlying component function, and transformed JSX runtime calls use that direct export instead of .make member access when available. That keeps existing source syntax and normal exports intact while producing a safer JS shape for server-
component toolchains.

Example before:

JsxRuntime.jsx(Sidebar$Foo.Provider.make, ...)

Example after:

JsxRuntime.jsx(Sidebar$Foo.Sidebar$Foo$Provider, ...)

Justification

The change crosses several compiler layers because the information needed for the rewrite starts in the JSX transform but
is only actionable during JS lowering.

<Sidebar.Provider> is initially just JSX syntax. To compile it to a direct export safely, the compiler needs to:

  • recognize that the tag came from a JSX component path, not ordinary value access,
  • preserve that distinction while translating the path into lambda,
  • emit hidden direct exports for nested component modules,
  • and finally rewrite transformed JSX runtime calls to use those exports.

That is why the PR touches:

  • JSX transform files, to mark nested JSX component paths and emit hoisted direct exports,
  • lambda translation files, to preserve enough path information through lowering,
  • and JS compilation files, to rewrite only transformed JSX callsites without affecting ordinary .make access.

So the surface behavior change is small, but the data needed to do it correctly spans multiple stages of the compiler pipeline.

Either way I marked it as a PoC since it might not be the best way to do it.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 12, 2026

Open in StackBlitz

rescript

npm i https://pkg.pr.new/rescript@8293

@rescript/darwin-arm64

npm i https://pkg.pr.new/@rescript/darwin-arm64@8293

@rescript/darwin-x64

npm i https://pkg.pr.new/@rescript/darwin-x64@8293

@rescript/linux-arm64

npm i https://pkg.pr.new/@rescript/linux-arm64@8293

@rescript/linux-x64

npm i https://pkg.pr.new/@rescript/linux-x64@8293

@rescript/runtime

npm i https://pkg.pr.new/@rescript/runtime@8293

@rescript/win32-x64

npm i https://pkg.pr.new/@rescript/win32-x64@8293

commit: 104c7ff

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