You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Each JSX DOM element adds ~30 KB to the .cmt: omitted optional record fields become typed-tree expressions #9148
Every JSX DOM element adds about 30 KB to its module's .cmt file. Its props record, JsxDOM.domProps, has 612 optional fields, and the typed tree holds a full expression node for each one the element doesn't set.
Where it comes from
When a record literal leaves out an optional field, the type checker fills that field with a synthesized None expression (compiler/ml/typecore.ml, the Overridden (…, option_none lbl.lbl_arg loc) case in the record-literal branch). Each synthesized expression has its own type instance, environment summary and location. They all reach the .cmt and are also translated to Lambda (compiler/ml/translcore.ml, Overridden (_, value) -> field (transl_exp value)), although the generated JavaScript contains only the props that were set.
A DOM element sets a few props and omits the remaining ~610, so each element adds about 610 expression nodes.
Measurements
These were taken on master 7249c11 with ordinary JSX (-bs-jsx 4), using a generated file of N elements like <div key="k1" className="c1"> {React.string("t1")} </div>. Times are user time, minimum of 3 runs; memory is peak RSS. The "without .cmt" columns use -bs-no-bin-annot.
Elements
Source
.cmt
Typed-tree expressions
Time with / without .cmt
Memory with / without .cmt
50
3 KB
1.5 MB
31,201
0.05 s / 0.03 s
51 MB / 32 MB
200
13 KB
6.3 MB
124,801
0.20 s / 0.12 s
121 MB / 64 MB
The 50 record literals hold 30,600 field entries, 612 each. The "without .cmt" numbers still include typing the synthesized expressions and translating them to Lambda.
Existing tests show the same pattern:
tests/tests/src/async_jsx.res: 455 bytes of source, 139 KB .cmt (305×).
tests/tests/src/jsx_preserve_test.res: 5.4 KB of source, 912 KB .cmt (168×). 15,200 expressions in the typed tree; 49 record literals with 17,146 field entries.
For comparison, .cmt files of the runtime, Belt and the test suite average 11.5× their source size (median 12×).
Impact
Every React project pays this in every module with DOM elements: compile time, peak memory, and the disk and read time of the .cmt files, which the editor analysis, reanalyze and gentype load.
The same happens for any record literal that omits many optional fields, not only domProps; JSX is simply where large optional-field records are everywhere.
Repro
// Gen.res, compiled with -bs-jsx 4 against @rescript/reactletelements= [
<divkey="k1"className="c1"> {React.string("t1")} </div>,
<divkey="k2"className="c2"> {React.string("t2")} </div>,
// … N elements
]
Compare the size of Gen.cmt with that of Gen.res, or count the expressions in the bsc -dtypedtree output.
Activity
Keep only the written fields of a record literal in the typed tree (#…
Keep only the written fields of a record literal in the typed tree (r…