Nested JSX components under React Server Components: derive flat exports from the signature
Maintainer thường phản hồi trong vòng 1 ngày
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 25/100
- Loại issue
- Tính năng
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- javascript, next.js, react
- Lĩnh vực
- compilers
Hướng nghiên cứu
Start by reviewing #8886 and its linked review, then trace how the compiler derives exports from signatures and rewrites nested .make paths. The issue proposes a signature-based naming rule, but leaves JSX-only versus all-read rewriting and dynamic imports open. Done means resolving those design questions and verifying the export behavior across the listed bundlers and optimization modes.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
🤖 Drafted with Claude (Claude Code), based on the review of #8886.
Problem
Nested components such as <Sidebar.Provider /> compile to a read through the module object:
import * as Sidebar from "./Sidebar.mjs";
JsxRuntime.jsx(Sidebar.Provider.make, …)
Under React Server Components, a server component can only use a client module's top-level exports. Sidebar.Provider is already a client reference, so reading .make on it fails ("You cannot dot into a client module from a server component"). Nested components from "use client" files therefore can't be used from server components (e.g. Next.js App Router).
Previous attempts:
- #8293: PoC rewriting JSX component paths to flat exports.
- #8402:
@res.hoistedFunctionplus a.cmjtable of hoisted exports, as the first step. - #8886: automatic hoisting of nested components on top of #8402.
Why the current approach is fragile
In #8402/#8886, whether a flat export exists depends on the producer's implementation, and it is recorded only in the producer's .cmj. Consumers compile against the .cmi, and both build systems rebuild dependents only when the .cmi changes. With a .resi, an edit that touches only the implementation (e.g. adding let make = React.memo(make)) removes Provider$make while the .cmi stays byte-identical. Already-compiled consumers then read undefined and React throws "Element type is invalid". The same applies to adding or removing @res.hoistedFunction in a .res file.
Most other issues found in the review of #8886 follow from the same design: components that are silently skipped, the JsxExternal rewrite (lost @deprecated/@genType, false reanalyze reports, rename bug, lost imports), property-read aliases that defeat tree-shaking, and linear lookups in the .cmj table. See https://github.com/rescript-lang/rescript/pull/8886#pullrequestreview-5562112336 for details.
Proposal
Use one fixed naming rule that both sides derive from the signature only:
Every
makein a nested module that is visible in the file's final signature is also exported asA$B$make.
- Producer: walk the signature after the
.resihas been applied. For each suchmake, addexport { <existing binding> as A$B$make }. This adds no statement, so tree-shaking is unaffected. Only a module with no static binding (a functor application or an unpacked first-class module) gets aconst A$B$make = A.B.makefallback. Relative externals are covered because the producer already exposes them as values. Package externals are unaffected because consumers import them directly. - Consumer: compile
Global.A.B.makeasGlobal.A$B$make, based purely on the shape of the path. No.cmjlookup. - Clash:
module A$B = {let make}next toA.B.makewould both map toA$B$make. Report a compile error in the producer. - Remove
@res.hoistedFunction, the.cmjtable of hoisted exports, and the related lookup code. They have only shipped inv13.0.0-alpha.6.
The export set then changes exactly when the .cmi changes, so rebuilding when the .cmi changes stays correct. No marker on the component definition, no identity checks, and no gating on "use client" are needed.
Open questions
- Rewrite every nested
.makeread, or only JSX element types? Rewriting every read is simpler and also covers components passed as props. Restricting it to JSX element types needs a marker attribute from the JSX transform. Either way,jest.spyOn(Lib.Trigger, "make")andvi.mockfactories written against the module object would no longer intercept, which should get a changelog note. - Dynamic imports:
module D = await Libbinds a module namespace, soD.Provider.makecould becomeD.Provider$makeas well. - To verify: unused extra export names are free in esbuild/Rolldown/webpack/Turbopack, and the consumer-side path rewrite works with cross-module optimization both on and off.
- Ngôn ngữ chính
- ReScript
- Star
- 7.5k
- Fork
- 483
- Merge trung bình
- 1 ngày 2 giờ
- Pull request đã merge (30 ngày)
- 147
Chuẩn bị môi trường
Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của rescript-lang/rescript
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 25/100
rescript-lang/rescript#8910 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
rescript-lang/rescript#8897 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 25/100
rescript-lang/rescript#8878 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
rescript-lang/rescript#8806 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
rescript-lang/rescript#8798 ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của rescript-lang/rescript
Issue tương tự
-
bug priority: P2
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 1 ngày
-
arrays asr pass enhancement format I/O low priority
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 86/100
Maintainer thường phản hồi trong vòng 1 ngày
-
bug grammar sev-0
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 86/100