Showing preview only (4,220K chars total). Download the full file or copy to clipboard to get everything.
Repository: ml-in-barcelona/server-reason-react
Branch: main
Commit: c9ab81197425
Files: 794
Total size: 3.9 MB
Directory structure:
gitextract_rs9v3350/
├── .dockerignore
├── .gitattributes
├── .githooks/
│ └── pre-push
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ ├── benchmark.yml
│ ├── ci.yml
│ └── docker.yml
├── .gitignore
├── .ocamlformat
├── CHANGES.md
├── Dockerfile
├── LICENSE.md
├── Makefile
├── README.md
├── arch/
│ ├── browser/
│ │ ├── .gitignore
│ │ ├── package.json
│ │ ├── public/
│ │ │ ├── index.html
│ │ │ └── manifest.json
│ │ └── src/
│ │ ├── index.css
│ │ └── index.js
│ └── server/
│ ├── head-ordering.js
│ ├── package.json
│ ├── react-dom-server-node-dom-props.js
│ ├── react-dom-server.js
│ ├── render-html-to-stream.js
│ ├── render-rsc-to-stream.js
│ ├── test-useid-edge-cases.js
│ └── test-useid.js
├── benchmark/
│ ├── Makefile
│ ├── README.md
│ ├── allocation.ml
│ ├── bench.ml
│ ├── dune
│ ├── frameworks/
│ │ ├── bun-native/
│ │ │ └── server.tsx
│ │ ├── hono-bun/
│ │ │ └── server.ts
│ │ ├── hono-node/
│ │ │ └── server.mjs
│ │ ├── node-express/
│ │ │ └── server.mjs
│ │ ├── node-fastify/
│ │ │ └── server.mjs
│ │ ├── package.json
│ │ ├── preact/
│ │ │ └── server.mjs
│ │ ├── render-bench.ts
│ │ └── shared/
│ │ ├── Blog.jsx
│ │ ├── Dashboard.jsx
│ │ ├── Ecommerce.jsx
│ │ ├── Form.jsx
│ │ ├── PropsHeavy.jsx
│ │ ├── cx.js
│ │ └── scenarios.jsx
│ ├── memory/
│ │ ├── dune
│ │ └── memory_bench.ml
│ ├── native/
│ │ ├── dune
│ │ └── server.re
│ ├── perf-work/
│ │ ├── PERF_NEXT.md
│ │ ├── README.md
│ │ ├── alloc-table500.txt
│ │ ├── alloc-wide500.txt
│ │ ├── alloc_profile.ml
│ │ ├── baseline-run1.txt
│ │ ├── baseline-run2.txt
│ │ ├── baseline-run3.txt
│ │ ├── cycles-out/
│ │ │ ├── callgrind-table100.annotate.stderr
│ │ │ ├── callgrind-table100.out
│ │ │ ├── callgrind-table100.stderr
│ │ │ ├── callgrind-table100.stdout
│ │ │ ├── callgrind-table100.txt
│ │ │ ├── callgrind-wide100.annotate.stderr
│ │ │ ├── callgrind-wide100.out
│ │ │ ├── callgrind-wide100.stderr
│ │ │ ├── callgrind-wide100.stdout
│ │ │ ├── callgrind-wide100.txt
│ │ │ ├── perf-stat-wide100.stdout
│ │ │ └── perf-stat-wide100.txt
│ │ ├── diff_styles.ml
│ │ ├── dump_html.ml
│ │ ├── dune
│ │ ├── dune_extra
│ │ ├── perf_profile.ml
│ │ ├── perf_profile.sh
│ │ ├── phase1-run1.txt
│ │ ├── phase1-run2.txt
│ │ ├── phase2-final.txt
│ │ ├── phase2-run1.txt
│ │ ├── phase2-run2.txt
│ │ ├── phase3-run1.txt
│ │ ├── phase3-run2.txt
│ │ ├── phase3b-run1.txt
│ │ ├── phase3b-run2.txt
│ │ ├── phase3c-run1.txt
│ │ ├── phase3c-run2.txt
│ │ ├── phase4-final.txt
│ │ ├── phase4-run1.txt
│ │ ├── phase7-final.txt
│ │ ├── style_alloc_bench.ml
│ │ ├── unification_bench.ml
│ │ ├── unified-experiment-runs.txt
│ │ └── unified-experiment.txt
│ ├── results/
│ │ └── .gitkeep
│ ├── runner/
│ │ ├── package.json
│ │ ├── runner.mjs
│ │ └── visualize.html
│ ├── scenarios/
│ │ ├── Blog.re
│ │ ├── Cx.re
│ │ ├── Dashboard.re
│ │ ├── DeepTree.re
│ │ ├── Ecommerce.re
│ │ ├── Form.re
│ │ ├── PropsHeavy.re
│ │ ├── ShallowTree.re
│ │ ├── Table.re
│ │ ├── Trivial.re
│ │ ├── WideTree.re
│ │ └── dune
│ └── streaming/
│ ├── dune
│ └── streaming_bench.ml
├── demo/
│ ├── README.md
│ ├── client/
│ │ ├── DummyRouterRSC.re
│ │ ├── HydrateRoot.re
│ │ ├── NestedRouterRSC.re
│ │ ├── RenderRoot.re
│ │ ├── ServerOnlyRSC.re
│ │ ├── SinglePageRSC.re
│ │ ├── build.mjs
│ │ ├── dune
│ │ └── package.json
│ ├── dream-nested-router/
│ │ ├── dune
│ │ ├── js/
│ │ │ ├── HistoryCache.re
│ │ │ ├── HistoryState.re
│ │ │ ├── VirtualHistory.re
│ │ │ └── dune
│ │ ├── native/
│ │ │ ├── README.md
│ │ │ ├── RouterRSC.re
│ │ │ ├── RouterRSC.rei
│ │ │ ├── dune
│ │ │ └── shared/
│ │ │ ├── DynamicParams.re
│ │ │ ├── NavigationResponse.re
│ │ │ ├── Route.re
│ │ │ ├── Router.re
│ │ │ └── Router.rei
│ │ └── test_router_rsc.ml
│ ├── dream-rsc/
│ │ ├── DreamRSC.re
│ │ ├── DreamRSC.rei
│ │ └── dune
│ ├── dune
│ ├── package.json
│ ├── server/
│ │ ├── db/
│ │ │ └── notes.json
│ │ ├── dune
│ │ ├── pages/
│ │ │ ├── Comments.re
│ │ │ ├── DummyRouterRSC.re
│ │ │ ├── Home.re
│ │ │ ├── NestedRouter.re
│ │ │ ├── NoteItem.re
│ │ │ ├── NoteList.re
│ │ │ ├── ServerOnlyRSC.re
│ │ │ ├── SidebarNote.re
│ │ │ └── SinglePageRSC.re
│ │ └── server.re
│ ├── styles.css
│ ├── tailwind.config.js
│ └── universal/
│ ├── js/
│ │ ├── Dream.re
│ │ └── dune
│ └── native/
│ ├── DB.re
│ ├── Date.re
│ ├── FunctionReferences.re
│ ├── FunctionReferences.rei
│ ├── Markdown.re
│ ├── SidebarNote.re
│ ├── dune
│ └── shared/
│ ├── Align.re
│ ├── App.re
│ ├── Arrow.re
│ ├── Button.re
│ ├── Context.re
│ ├── Counter.re
│ ├── Cx.re
│ ├── Debug_props.re
│ ├── DeleteNoteButton.re
│ ├── DemoLayout.re
│ ├── Document.re
│ ├── DummyClientRouter.re
│ ├── Expander.re
│ ├── GlobalStyles.re
│ ├── Hr.re
│ ├── InputText.re
│ ├── Link.re
│ ├── NestedRouter_CreateNoteButton.re
│ ├── NestedRouter_DeleteNoteButton.re
│ ├── NestedRouter_EditButton.re
│ ├── NestedRouter_NoteEditor.re
│ ├── NestedRouter_NoteItem.re
│ ├── NestedRouter_NoteList.re
│ ├── NestedRouter_SearchField.re
│ ├── NestedRouter_SidebarNote.re
│ ├── NestedRouter_SidebarNoteContent.re
│ ├── Note.re
│ ├── NoteEditor.re
│ ├── NoteListSkeleton.re
│ ├── NotePreview.re
│ ├── NoteSkeleton.re
│ ├── Promise_renderer.re
│ ├── RR.re
│ ├── RequestContextDemo.re
│ ├── Routes.re
│ ├── Row.re
│ ├── SearchField.re
│ ├── ServerActionFromPropsClient.re
│ ├── ServerActionWithError.re
│ ├── ServerActionWithFormData.re
│ ├── ServerActionWithFormDataFormAction.re
│ ├── ServerActionWithFormDataServer.re
│ ├── ServerActionWithFormDataWithArg.re
│ ├── ServerActionWithOptionalArg.re
│ ├── ServerActionWithSimpleResponse.re
│ ├── ServerFunctions.re
│ ├── SidebarNoteContent.re
│ ├── Spinner.re
│ ├── Stack.re
│ ├── Static_small.re
│ ├── Text.re
│ ├── Textarea.re
│ └── Theme.re
├── documentation/
│ ├── browser_ppx.mld
│ ├── dune
│ ├── externals-melange-attributes.mld
│ ├── get-started.mld
│ ├── how-to-organise-universal-code.mld
│ ├── index.mld
│ ├── ssr-and-hydration.mld
│ └── universal-code.mld
├── dune
├── dune-project
├── fly.toml
├── packages/
│ ├── Belt/
│ │ ├── src/
│ │ │ ├── Belt.re
│ │ │ ├── Belt_Array.ml
│ │ │ ├── Belt_Array.mli
│ │ │ ├── Belt_Float.ml
│ │ │ ├── Belt_Float.mli
│ │ │ ├── Belt_HashMap.ml
│ │ │ ├── Belt_HashMap.mli
│ │ │ ├── Belt_HashMapInt.ml
│ │ │ ├── Belt_HashMapInt.mli
│ │ │ ├── Belt_HashMapString.ml
│ │ │ ├── Belt_HashMapString.mli
│ │ │ ├── Belt_HashSet.ml
│ │ │ ├── Belt_HashSet.mli
│ │ │ ├── Belt_HashSetInt.ml
│ │ │ ├── Belt_HashSetInt.mli
│ │ │ ├── Belt_HashSetString.ml
│ │ │ ├── Belt_HashSetString.mli
│ │ │ ├── Belt_Id.ml
│ │ │ ├── Belt_Id.mli
│ │ │ ├── Belt_Int.ml
│ │ │ ├── Belt_Int.mli
│ │ │ ├── Belt_List.ml
│ │ │ ├── Belt_List.mli
│ │ │ ├── Belt_Map.ml
│ │ │ ├── Belt_Map.mli
│ │ │ ├── Belt_MapDict.ml
│ │ │ ├── Belt_MapDict.mli
│ │ │ ├── Belt_MapInt.ml
│ │ │ ├── Belt_MapInt.mli
│ │ │ ├── Belt_MapString.ml
│ │ │ ├── Belt_MapString.mli
│ │ │ ├── Belt_MutableMap.ml
│ │ │ ├── Belt_MutableMap.mli
│ │ │ ├── Belt_MutableMapInt.ml
│ │ │ ├── Belt_MutableMapInt.mli
│ │ │ ├── Belt_MutableMapString.ml
│ │ │ ├── Belt_MutableMapString.mli
│ │ │ ├── Belt_MutableQueue.ml
│ │ │ ├── Belt_MutableQueue.mli
│ │ │ ├── Belt_MutableSet.ml
│ │ │ ├── Belt_MutableSet.mli
│ │ │ ├── Belt_MutableSetInt.ml
│ │ │ ├── Belt_MutableSetInt.mli
│ │ │ ├── Belt_MutableSetString.ml
│ │ │ ├── Belt_MutableSetString.mli
│ │ │ ├── Belt_MutableStack.ml
│ │ │ ├── Belt_MutableStack.mli
│ │ │ ├── Belt_Option.ml
│ │ │ ├── Belt_Option.mli
│ │ │ ├── Belt_Range.ml
│ │ │ ├── Belt_Range.mli
│ │ │ ├── Belt_Result.ml
│ │ │ ├── Belt_Result.mli
│ │ │ ├── Belt_Set.ml
│ │ │ ├── Belt_Set.mli
│ │ │ ├── Belt_SetDict.ml
│ │ │ ├── Belt_SetDict.mli
│ │ │ ├── Belt_SetInt.ml
│ │ │ ├── Belt_SetInt.mli
│ │ │ ├── Belt_SetString.ml
│ │ │ ├── Belt_SetString.mli
│ │ │ ├── Belt_SortArray.ml
│ │ │ ├── Belt_SortArray.mli
│ │ │ ├── Belt_SortArrayInt.ml
│ │ │ ├── Belt_SortArrayInt.mli
│ │ │ ├── Belt_SortArrayString.ml
│ │ │ ├── Belt_SortArrayString.mli
│ │ │ ├── Belt_internalAVLset.ml
│ │ │ ├── Belt_internalAVLset.mli
│ │ │ ├── Belt_internalAVLtree.ml
│ │ │ ├── Belt_internalAVLtree.mli
│ │ │ ├── Belt_internalBuckets.ml
│ │ │ ├── Belt_internalBuckets.mli
│ │ │ ├── Belt_internalBucketsType.ml
│ │ │ ├── Belt_internalBucketsType.mli
│ │ │ ├── Belt_internalMapInt.ml
│ │ │ ├── Belt_internalMapString.ml
│ │ │ ├── Belt_internalSetBuckets.ml
│ │ │ ├── Belt_internalSetBuckets.mli
│ │ │ ├── Belt_internalSetInt.ml
│ │ │ ├── Belt_internalSetString.ml
│ │ │ ├── caml_hash.ml
│ │ │ ├── dune
│ │ │ └── stubs.c
│ │ └── test/
│ │ ├── Test_Belt_Array.ml
│ │ ├── Test_Belt_Float.ml
│ │ ├── Test_Belt_HashMap.ml
│ │ ├── Test_Belt_HashMap_Int.ml
│ │ ├── Test_Belt_HashMap_String.ml
│ │ ├── Test_Belt_HashSet_Int.ml
│ │ ├── Test_Belt_HashSet_String.ml
│ │ ├── Test_Belt_Int.ml
│ │ ├── Test_Belt_List.ml
│ │ ├── Test_Belt_Map.ml
│ │ ├── Test_Belt_Map_Dict.ml
│ │ ├── Test_Belt_Map_Int.ml
│ │ ├── Test_Belt_Map_String.ml
│ │ ├── Test_Belt_MutableMap.ml
│ │ ├── Test_Belt_MutableMap_Int.ml
│ │ ├── Test_Belt_MutableMap_String.ml
│ │ ├── Test_Belt_MutableQueue.ml
│ │ ├── Test_Belt_MutableSet.ml
│ │ ├── Test_Belt_MutableSet_Int.ml
│ │ ├── Test_Belt_MutableSet_String.ml
│ │ ├── Test_Belt_MutableStack.ml
│ │ ├── Test_Belt_Option.ml
│ │ ├── Test_Belt_Result.ml
│ │ ├── Test_Belt_Set.ml
│ │ ├── Test_Belt_Set_Dict.ml
│ │ ├── Test_Belt_Set_Int.ml
│ │ ├── Test_Belt_Set_String.ml
│ │ ├── Test_Belt_SortArray.ml
│ │ ├── Test_Belt_SortArray_Int.ml
│ │ ├── Test_Belt_SortArray_String.ml
│ │ ├── Test_Belt_Support.ml
│ │ ├── benchmark.ml
│ │ ├── dune
│ │ └── test.ml
│ ├── Dom/
│ │ ├── Dom.ml
│ │ ├── Dom_storage.ml
│ │ └── dune
│ ├── Js/
│ │ ├── lib/
│ │ │ ├── Js.ml
│ │ │ ├── Js.mli
│ │ │ ├── Js_array.ml
│ │ │ ├── Js_array.mli
│ │ │ ├── Js_bigint.ml
│ │ │ ├── Js_bigint.mli
│ │ │ ├── Js_console.ml
│ │ │ ├── Js_console.mli
│ │ │ ├── Js_date.ml
│ │ │ ├── Js_date.mli
│ │ │ ├── Js_dict.ml
│ │ │ ├── Js_dict.mli
│ │ │ ├── Js_exn.ml
│ │ │ ├── Js_exn.mli
│ │ │ ├── Js_float.ml
│ │ │ ├── Js_float.mli
│ │ │ ├── Js_formdata.ml
│ │ │ ├── Js_formdata.mli
│ │ │ ├── Js_global.ml
│ │ │ ├── Js_global.mli
│ │ │ ├── Js_int.ml
│ │ │ ├── Js_int.mli
│ │ │ ├── Js_internal.ml
│ │ │ ├── Js_internal.mli
│ │ │ ├── Js_json.ml
│ │ │ ├── Js_json.mli
│ │ │ ├── Js_map.ml
│ │ │ ├── Js_map.mli
│ │ │ ├── Js_math.ml
│ │ │ ├── Js_math.mli
│ │ │ ├── Js_null.ml
│ │ │ ├── Js_null.mli
│ │ │ ├── Js_nullable.ml
│ │ │ ├── Js_nullable.mli
│ │ │ ├── Js_obj.ml
│ │ │ ├── Js_obj.mli
│ │ │ ├── Js_promise.ml
│ │ │ ├── Js_promise.mli
│ │ │ ├── Js_re.ml
│ │ │ ├── Js_re.mli
│ │ │ ├── Js_set.ml
│ │ │ ├── Js_set.mli
│ │ │ ├── Js_string.ml
│ │ │ ├── Js_string.mli
│ │ │ ├── Js_typed_array.ml
│ │ │ ├── Js_typed_array.mli
│ │ │ ├── Js_typed_array2.ml
│ │ │ ├── Js_typed_array2.mli
│ │ │ ├── Js_types.ml
│ │ │ ├── Js_types.mli
│ │ │ ├── Js_undefined.ml
│ │ │ ├── Js_undefined.mli
│ │ │ ├── Js_vector.ml
│ │ │ ├── Js_vector.mli
│ │ │ ├── Js_weakmap.ml
│ │ │ ├── Js_weakmap.mli
│ │ │ ├── Js_weakset.ml
│ │ │ ├── Js_weakset.mli
│ │ │ └── dune
│ │ └── test/
│ │ ├── bigint_tests/
│ │ │ ├── arithmetic.ml
│ │ │ ├── as_int_n.ml
│ │ │ ├── as_uint_n.ml
│ │ │ ├── bitwise.ml
│ │ │ ├── comparison.ml
│ │ │ ├── constructor.ml
│ │ │ ├── conversion.ml
│ │ │ └── prototype.ml
│ │ ├── date_tests/
│ │ │ ├── getters.ml
│ │ │ ├── local_getters.ml
│ │ │ ├── now.ml
│ │ │ ├── parse.ml
│ │ │ ├── setters.ml
│ │ │ ├── to_iso_string.ml
│ │ │ ├── to_string.ml
│ │ │ └── utc.ml
│ │ ├── dune
│ │ ├── helpers.ml
│ │ ├── number_tests/
│ │ │ ├── is_finite.ml
│ │ │ ├── is_integer.ml
│ │ │ ├── is_nan.ml
│ │ │ ├── parse_float.ml
│ │ │ ├── parse_int.ml
│ │ │ ├── to_exponential.ml
│ │ │ ├── to_precision.ml
│ │ │ └── to_string.ml
│ │ ├── regexp_tests/
│ │ │ ├── dotall.ml
│ │ │ ├── named_groups.ml
│ │ │ └── unicode.ml
│ │ ├── string_tests/
│ │ │ ├── normalize.ml
│ │ │ └── search.ml
│ │ ├── test.ml
│ │ └── undefined_tests/
│ │ └── undefined.ml
│ ├── browser-ppx/
│ │ ├── dune
│ │ ├── ppx.ml
│ │ └── tests/
│ │ ├── at_browser_only.t
│ │ ├── at_platform.t
│ │ ├── dune
│ │ ├── pexp_apply.t
│ │ ├── pexp_constraint_re.t
│ │ ├── pexp_fun.t
│ │ ├── pexp_fun_with_vb.t
│ │ ├── pexp_function.t
│ │ ├── pexp_ident.t
│ │ ├── playground.t/
│ │ │ ├── input.re
│ │ │ └── run.t
│ │ ├── preprocess.t
│ │ ├── standalone.ml
│ │ ├── structure_item.t
│ │ ├── structure_item_re.t
│ │ ├── switch-platform.t/
│ │ │ ├── input.re
│ │ │ └── run.t
│ │ └── use_effect.t
│ ├── esbuild-plugin/
│ │ ├── dune
│ │ ├── extract_client_components.ml
│ │ ├── package.json
│ │ ├── plugin.mjs
│ │ └── test/
│ │ ├── ClientComponent.js
│ │ ├── ClientComponentWithModule.js
│ │ ├── ServerFunction.js
│ │ ├── dune
│ │ └── run.t
│ ├── expand-styles-attribute/
│ │ ├── dune
│ │ ├── expand_styles_attribute.ml
│ │ └── test/
│ │ ├── dune
│ │ └── test.ml
│ ├── fetch/
│ │ ├── Fetch.ml
│ │ └── dune
│ ├── html/
│ │ ├── Html.ml
│ │ └── dune
│ ├── melange.ppx/
│ │ ├── base32/
│ │ │ ├── LICENSES/
│ │ │ │ └── ISC.txt
│ │ │ ├── README.md
│ │ │ └── lib/
│ │ │ ├── base32.ml
│ │ │ ├── base32.mli
│ │ │ └── dune
│ │ ├── derive_util.ml
│ │ ├── double_hash.ml
│ │ ├── dune
│ │ ├── get_set.ml
│ │ ├── js_converter.ml
│ │ ├── js_properties.ml
│ │ ├── pipe_first.ml
│ │ ├── ppx.ml
│ │ ├── regex.ml
│ │ ├── tests/
│ │ │ ├── dune
│ │ │ ├── external.t
│ │ │ ├── input.ml
│ │ │ ├── jsConverter.t
│ │ │ ├── jsProperties.t
│ │ │ ├── mel_as.t
│ │ │ ├── mel_module.t
│ │ │ ├── mel_obj.t
│ │ │ ├── mel_raw.t
│ │ │ ├── mel_send.t
│ │ │ ├── mel_send_pipe.t
│ │ │ ├── pipe_first.t/
│ │ │ │ ├── input.ml
│ │ │ │ └── run.t
│ │ │ ├── private.t
│ │ │ ├── regex.t/
│ │ │ │ ├── input.ml
│ │ │ │ └── run.t
│ │ │ ├── standalone.ml
│ │ │ └── string_interpolation.t
│ │ └── xxhash/
│ │ ├── XXH64.ml
│ │ ├── dune
│ │ └── test_xxh64.ml
│ ├── promise/
│ │ ├── js/
│ │ │ ├── dune
│ │ │ ├── promise.re
│ │ │ └── promise.rei
│ │ └── native/
│ │ ├── dune
│ │ ├── promise.re
│ │ └── promise.rei
│ ├── react/
│ │ ├── src/
│ │ │ ├── React.ml
│ │ │ ├── React.mli
│ │ │ ├── ReactEvent.ml
│ │ │ ├── ReasonReactRouter.ml
│ │ │ ├── ReasonReactRouter.mli
│ │ │ └── dune
│ │ └── test/
│ │ ├── dune
│ │ ├── test.ml
│ │ ├── test_cloneElement.ml
│ │ └── test_react.ml
│ ├── react-server-dom-esbuild/
│ │ ├── ReactServerDOMEsbuild.js
│ │ ├── ReactServerDOMEsbuild.re
│ │ ├── dune
│ │ └── package.json
│ ├── reactDom/
│ │ ├── src/
│ │ │ ├── Push_stream.ml
│ │ │ ├── ReactDOM.ml
│ │ │ ├── ReactDOM.mli
│ │ │ ├── ReactDOMStyle.ml
│ │ │ ├── ReactDOMStyle.mli
│ │ │ ├── ReactServerDOM.ml
│ │ │ ├── ReactServerDOM.mli
│ │ │ └── dune
│ │ └── test/
│ │ ├── dune
│ │ ├── test.ml
│ │ ├── test_RSC_decoders.ml
│ │ ├── test_RSC_html.ml
│ │ ├── test_RSC_html_shell.ml
│ │ ├── test_RSC_model.ml
│ │ ├── test_reactDOMStyle.ml
│ │ ├── test_renderToStaticMarkup.ml
│ │ ├── test_renderToStream.ml
│ │ ├── test_renderToString.ml
│ │ ├── test_useId.ml
│ │ └── test_write_to_buffer.ml
│ ├── rsc/
│ │ ├── README.md
│ │ ├── js/
│ │ │ ├── RSC.ml
│ │ │ ├── RSC.mli
│ │ │ └── dune
│ │ ├── native/
│ │ │ ├── RSC.ml
│ │ │ ├── RSC.mli
│ │ │ └── dune
│ │ ├── ppx_common/
│ │ │ ├── dune
│ │ │ ├── ppx_deriving_tools.ml
│ │ │ ├── ppx_deriving_tools.mli
│ │ │ └── rsc_deriving_common.ml
│ │ ├── ppx_js/
│ │ │ ├── dune
│ │ │ └── ppx_deriving_rsc_js.ml
│ │ └── ppx_native/
│ │ ├── dune
│ │ └── ppx_deriving_rsc_native.ml
│ ├── runtime/
│ │ ├── Runtime.ml
│ │ ├── Runtime.mli
│ │ └── dune
│ ├── server-reason-react-ppx/
│ │ ├── DomProps.ml
│ │ ├── DomProps.mli
│ │ ├── Style_rewrite.ml
│ │ ├── cram/
│ │ │ ├── client-component-e2e.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── client-component-no-props.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── client-component-on-the-client-nested.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── client-component-on-the-client.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── client-component-on-the-server.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── client-component-with-fn-error.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── client-props-decoding.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── component-definition-at-toplevel.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── component-definition.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── component-defintion-signatures.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── dune
│ │ │ ├── dune-describe-pp.sh
│ │ │ ├── ensure-attributes-are-present.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── external.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── functor.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── jsx-fragment.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── locations/
│ │ │ │ ├── input.re
│ │ │ │ └── run
│ │ │ ├── lower-call-missing-prop.t/
│ │ │ │ ├── input.re
│ │ │ │ ├── run.t
│ │ │ │ └── wrong-prop.re
│ │ │ ├── lower-call-reserved-prop.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── lower-calls.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── ppx.sh
│ │ │ ├── reason.expected
│ │ │ ├── server-client-props.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── server-function-on-client.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── server-function-on-server.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── shared-folder-prefix-melange.t/
│ │ │ │ ├── js/
│ │ │ │ │ └── input.re
│ │ │ │ └── run.t
│ │ │ ├── shared-folder-prefix-native.t/
│ │ │ │ ├── native/
│ │ │ │ │ └── input.ml
│ │ │ │ └── run.t
│ │ │ ├── standalone.ml
│ │ │ ├── styles.t/
│ │ │ │ ├── input.re
│ │ │ │ └── run.t
│ │ │ ├── temp.ml
│ │ │ ├── upper-calls-ocaml.t/
│ │ │ │ ├── input.ml
│ │ │ │ └── run.t
│ │ │ └── upper-calls.t/
│ │ │ ├── input.re
│ │ │ └── run.t
│ │ ├── dune
│ │ ├── server_reason_react_ppx.ml
│ │ ├── static_analysis.ml
│ │ └── test/
│ │ ├── dune
│ │ └── test.re
│ ├── url/
│ │ ├── URL.rei
│ │ ├── js/
│ │ │ ├── URL.re
│ │ │ └── dune
│ │ ├── native/
│ │ │ ├── URL.re
│ │ │ └── dune
│ │ └── test/
│ │ ├── dune
│ │ └── test_native.re
│ └── webapi/
│ ├── src/
│ │ ├── Canvas/
│ │ │ ├── Webapi__Canvas__Canvas2d.re
│ │ │ └── Webapi__Canvas__WebGl.re
│ │ ├── Dom/
│ │ │ ├── Webapi__Dom__AnimationEvent.re
│ │ │ ├── Webapi__Dom__Attr.re
│ │ │ ├── Webapi__Dom__BeforeUnloadEvent.re
│ │ │ ├── Webapi__Dom__CdataSection.re
│ │ │ ├── Webapi__Dom__CharacterData.re
│ │ │ ├── Webapi__Dom__ChildNode.re
│ │ │ ├── Webapi__Dom__ClipboardEvent.re
│ │ │ ├── Webapi__Dom__CloseEvent.re
│ │ │ ├── Webapi__Dom__Comment.re
│ │ │ ├── Webapi__Dom__CompositionEvent.re
│ │ │ ├── Webapi__Dom__CssStyleDeclaration.re
│ │ │ ├── Webapi__Dom__CustomEvent.re
│ │ │ ├── Webapi__Dom__Document.re
│ │ │ ├── Webapi__Dom__DocumentFragment.re
│ │ │ ├── Webapi__Dom__DocumentOrShadowRoot.re
│ │ │ ├── Webapi__Dom__DocumentType.re
│ │ │ ├── Webapi__Dom__DomImplementation.re
│ │ │ ├── Webapi__Dom__DomRect.re
│ │ │ ├── Webapi__Dom__DomStringMap.re
│ │ │ ├── Webapi__Dom__DomTokenList.re
│ │ │ ├── Webapi__Dom__DragEvent.re
│ │ │ ├── Webapi__Dom__Element.re
│ │ │ ├── Webapi__Dom__ErrorEvent.re
│ │ │ ├── Webapi__Dom__Event.re
│ │ │ ├── Webapi__Dom__EventTarget.re
│ │ │ ├── Webapi__Dom__FocusEvent.re
│ │ │ ├── Webapi__Dom__GlobalEventHandlers.re
│ │ │ ├── Webapi__Dom__History.re
│ │ │ ├── Webapi__Dom__HtmlCollection.re
│ │ │ ├── Webapi__Dom__HtmlDocument.re
│ │ │ ├── Webapi__Dom__HtmlElement.re
│ │ │ ├── Webapi__Dom__HtmlFormElement.re
│ │ │ ├── Webapi__Dom__HtmlImageElement.re
│ │ │ ├── Webapi__Dom__HtmlInputElement.re
│ │ │ ├── Webapi__Dom__IdbVersionChangeEvent.re
│ │ │ ├── Webapi__Dom__Image.re
│ │ │ ├── Webapi__Dom__InputEvent.re
│ │ │ ├── Webapi__Dom__KeyboardEvent.re
│ │ │ ├── Webapi__Dom__Location.re
│ │ │ ├── Webapi__Dom__MouseEvent.re
│ │ │ ├── Webapi__Dom__MutationObserver.re
│ │ │ ├── Webapi__Dom__MutationRecord.re
│ │ │ ├── Webapi__Dom__NamedNodeMap.re
│ │ │ ├── Webapi__Dom__Node.re
│ │ │ ├── Webapi__Dom__NodeFilter.re
│ │ │ ├── Webapi__Dom__NodeIterator.re
│ │ │ ├── Webapi__Dom__NodeList.re
│ │ │ ├── Webapi__Dom__NonDocumentTypeChildNode.re
│ │ │ ├── Webapi__Dom__NonElementParentNode.re
│ │ │ ├── Webapi__Dom__PageTransitionEvent.re
│ │ │ ├── Webapi__Dom__ParentNode.re
│ │ │ ├── Webapi__Dom__PointerEvent.re
│ │ │ ├── Webapi__Dom__PopStateEvent.re
│ │ │ ├── Webapi__Dom__ProcessingInstruction.re
│ │ │ ├── Webapi__Dom__ProgressEvent.re
│ │ │ ├── Webapi__Dom__Range.re
│ │ │ ├── Webapi__Dom__RelatedEvent.re
│ │ │ ├── Webapi__Dom__Selection.re
│ │ │ ├── Webapi__Dom__ShadowRoot.re
│ │ │ ├── Webapi__Dom__Slotable.re
│ │ │ ├── Webapi__Dom__StorageEvent.re
│ │ │ ├── Webapi__Dom__SvgZoomEvent.re
│ │ │ ├── Webapi__Dom__Text.re
│ │ │ ├── Webapi__Dom__TimeEvent.re
│ │ │ ├── Webapi__Dom__TouchEvent.re
│ │ │ ├── Webapi__Dom__TrackEvent.re
│ │ │ ├── Webapi__Dom__TransitionEvent.re
│ │ │ ├── Webapi__Dom__TreeWalker.re
│ │ │ ├── Webapi__Dom__Types.re
│ │ │ ├── Webapi__Dom__UiEvent.re
│ │ │ ├── Webapi__Dom__ValidityState.re
│ │ │ ├── Webapi__Dom__WebGlContextEvent.re
│ │ │ ├── Webapi__Dom__WheelEvent.re
│ │ │ └── Webapi__Dom__Window.re
│ │ ├── ResizeObserver/
│ │ │ └── Webapi__ResizeObserver__ResizeObserverEntry.re
│ │ ├── Webapi.re
│ │ ├── Webapi__Base64.re
│ │ ├── Webapi__Blob.re
│ │ ├── Webapi__Canvas.re
│ │ ├── Webapi__Dom.re
│ │ ├── Webapi__File.re
│ │ ├── Webapi__Performance.re
│ │ ├── Webapi__ReadableStream.re
│ │ ├── Webapi__ResizeObserver.re
│ │ ├── Webapi__Url.re
│ │ └── dune
│ └── tests/
│ ├── Canvas/
│ │ └── Webapi__Canvas__Canvas2d__test.re
│ ├── Dom/
│ │ ├── Webapi__Dom__AnimationEvent__test.re
│ │ ├── Webapi__Dom__BeforeUnloadEvent__test.re
│ │ ├── Webapi__Dom__ClipboardEvent__test.re
│ │ ├── Webapi__Dom__CloseEvent__test.re
│ │ ├── Webapi__Dom__CompositionEvent__test.re
│ │ ├── Webapi__Dom__CustomEvent__test.re
│ │ ├── Webapi__Dom__Document__test.re
│ │ ├── Webapi__Dom__DomStringMap__test.re
│ │ ├── Webapi__Dom__DomTokenList__test.re
│ │ ├── Webapi__Dom__DragEvent__test.re
│ │ ├── Webapi__Dom__Element__test.re
│ │ ├── Webapi__Dom__ErrorEvent__test.re
│ │ ├── Webapi__Dom__EventTarget__test.re
│ │ ├── Webapi__Dom__Event__test.re
│ │ ├── Webapi__Dom__FocusEvent__test.re
│ │ ├── Webapi__Dom__GlobalEventHandlers__test.re
│ │ ├── Webapi__Dom__History__test.re
│ │ ├── Webapi__Dom__HtmlDocument__test.re
│ │ ├── Webapi__Dom__HtmlElement__test.re
│ │ ├── Webapi__Dom__HtmlFormElement__test.re
│ │ ├── Webapi__Dom__IdbVersionChangeEvent__test.re
│ │ ├── Webapi__Dom__Image__test.re
│ │ ├── Webapi__Dom__InputEvent__test.re
│ │ ├── Webapi__Dom__KeyboardEvent__test.re
│ │ ├── Webapi__Dom__Location__test.re
│ │ ├── Webapi__Dom__MouseEvent__test.re
│ │ ├── Webapi__Dom__NodeList__test.re
│ │ ├── Webapi__Dom__Node__test.re
│ │ ├── Webapi__Dom__PageTransitionEvent__test.re
│ │ ├── Webapi__Dom__PointerEvent__test.re
│ │ ├── Webapi__Dom__PopStateEvent__test.re
│ │ ├── Webapi__Dom__ProgressEvent__test.re
│ │ ├── Webapi__Dom__Range__test.re
│ │ ├── Webapi__Dom__RelatedEvent__test.re
│ │ ├── Webapi__Dom__Selection__test.re
│ │ ├── Webapi__Dom__StorageEvent__test.re
│ │ ├── Webapi__Dom__SvgZoomEvent__test.re
│ │ ├── Webapi__Dom__Text__test.re
│ │ ├── Webapi__Dom__TimeEvent__test.re
│ │ ├── Webapi__Dom__TouchEvent__test.re
│ │ ├── Webapi__Dom__TrackEvent__test.re
│ │ ├── Webapi__Dom__TransitionEvent__test.re
│ │ ├── Webapi__Dom__UiEvent__test.re
│ │ ├── Webapi__Dom__WebGlContextEvent__test.re
│ │ ├── Webapi__Dom__WheelEvent__test.re
│ │ └── Webapi__Dom__Window__test.re
│ ├── Webapi__Base64__test.re
│ ├── Webapi__Blob__test.re
│ ├── Webapi__File__test.re
│ ├── Webapi__Performace__test.re
│ ├── Webapi__ReadableStream__test.re
│ ├── Webapi__ResizeObserver__test.re
│ ├── Webapi__Url__test.re
│ ├── _dune
│ └── testHelpers.re
├── server-reason-react.opam
└── server-reason-react.opam.template
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
**/_build
**/_opam
**/_opam_*
_opam*
node_modules/
**/node_modules/
compare/
**/.merlin
*.install
.git/
README.md
CHANGELOG.md
benchmark/
arch/
.github/
================================================
FILE: .gitattributes
================================================
*.re linguist-language=Reason
*.rei linguist-language=Reason
*.ml linguist-language=OCaml
*.mli linguist-language=OCaml
*.mll linguist-language=OCaml
*.mly linguist-language=OCaml
================================================
FILE: .githooks/pre-push
================================================
#!/bin/bash
if ! ( make format-check ); then
echo "some files are not properly formatted, refusing to push"
exit 1
fi
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: davesnx jchavarri
================================================
FILE: .github/workflows/benchmark.yml
================================================
name: Framework Comparison
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
frameworks:
description: 'Frameworks to test (comma-separated, or "all")'
required: false
default: 'all'
scenarios:
description: 'Scenarios to run (comma-separated, or "all")'
required: false
default: 'trivial,table100,table500'
jobs:
benchmark-frameworks:
name: Compare Frameworks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup OCaml
uses: ocaml/setup-ocaml@v3
with:
ocaml-compiler: 5.2.x
dune-cache: true
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Setup Bun
uses: oven-sh/setup-bun@v1
- name: Install wrk
run: |
sudo apt-get update
sudo apt-get install -y wrk
- name: Install OCaml dependencies
run: opam install . --deps-only -y
- name: Build native server
run: opam exec -- dune build benchmark/native/server.exe --profile=release
- name: Install JS framework dependencies
working-directory: benchmark/frameworks
run: npm install
- name: Install runner dependencies
working-directory: benchmark/runner
run: npm install
- name: Start native server
run: |
opam exec -- _build/default/benchmark/native/server.exe &
sleep 2
- name: Run framework comparison
working-directory: benchmark/runner
run: |
FRAMEWORKS="${{ github.event.inputs.frameworks || 'all' }}"
SCENARIOS="${{ github.event.inputs.scenarios || 'trivial,table100,table500' }}"
ARGS=""
if [ "$FRAMEWORKS" != "all" ]; then
ARGS="$ARGS --frameworks $FRAMEWORKS"
fi
if [ "$SCENARIOS" != "all" ]; then
ARGS="$ARGS --scenarios $SCENARIOS"
fi
node runner.mjs $ARGS
- name: Upload results
uses: actions/upload-artifact@v4
with:
name: framework-comparison-${{ github.sha }}
path: benchmark/runner/results/
retention-days: 90
- name: Add results to summary
run: |
echo "## Framework Comparison Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
LATEST=$(ls -t benchmark/runner/results/*.md 2>/dev/null | head -1)
if [ -n "$LATEST" ]; then
cat "$LATEST" >> $GITHUB_STEP_SUMMARY
else
echo "No results generated" >> $GITHUB_STEP_SUMMARY
fi
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
branches:
- main
tags:
- '*'
pull_request:
branches:
- main
env:
DUNE_PROFILE: release
OCAMLRUNPARAM: b
permissions:
contents: write
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
defaults:
run:
shell: bash -xeuo pipefail {0}
jobs:
build:
name: Build and test
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- macos-latest
- ubuntu-latest
# - windows-latest
ocaml-compiler:
- 4.14.1
- 5.4.0
steps:
- uses: actions/checkout@v4
- name: Use OCaml ${{ matrix.ocaml-compiler }}
uses: ocaml/setup-ocaml@v3.4.5
with:
ocaml-compiler: ${{ matrix.ocaml-compiler }}
dune-cache: false
opam-disable-sandboxing: true
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install opam deps
run: make install
- name: Install npm deps
run: make install-npm
- name: Pin dependencies
run: make pin
- name: Build
run: make build
- name: Check formatting
if: matrix.ocaml-compiler != '4.14.1'
run: make format-check
- name: Run tests
run: make test
- name: Generate docs
if: github.ref == 'refs/heads/main' && matrix.os == 'ubuntu-latest' && matrix.ocaml-compiler == '5.4.0'
run: |
opam install -y odoc-driver
make docs
- name: Upload docs artifact
if: github.ref == 'refs/heads/main' && matrix.os == 'ubuntu-latest' && matrix.ocaml-compiler == '5.4.0'
uses: actions/upload-artifact@v4
with:
name: documentation
path: _html
retention-days: 1
- name: Run benchmarks
run: make bench
- name: Run benchmarks as JSON
if: matrix.os == 'ubuntu-latest' && matrix.ocaml-compiler == '5.4.0'
run: make bench-json
- name: Store benchmark result
if: matrix.os == 'ubuntu-latest' && matrix.ocaml-compiler == '5.4.0'
uses: benchmark-action/github-action-benchmark@v1
with:
name: server-reason-react Benchmarks
tool: 'customBiggerIsBetter'
output-file-path: bench_results.json
github-token: ${{ secrets.GITHUB_TOKEN }}
auto-push: ${{ github.ref == 'refs/heads/main' }}
gh-pages-branch: gh-pages
benchmark-data-dir-path: dev/bench
comment-always: true
fail-on-alert: true
comment-on-alert: true
alert-comment-cc-users: '@davesnx'
- name: Install dune-release
if: startsWith(github.ref, 'refs/tags/') && matrix.os == 'ubuntu-latest' && matrix.ocaml-compiler == '5.4.0'
run: opam install dune-release -y
- name: Release
uses: davesnx/dune-release-action@v0.2.14
if: startsWith(github.ref, 'refs/tags/') && matrix.os == 'ubuntu-latest' && matrix.ocaml-compiler == '5.4.0'
with:
packages: 'server-reason-react'
changelog: './CHANGES.md'
github-token: ${{ secrets.GH_TOKEN }}
publish-docs:
name: Publish documentation
needs: build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment:
name: github-pages
url: https://ml-in-barcelona.github.io/server-reason-react
permissions:
contents: write
pages: write
id-token: write
steps:
- uses: actions/checkout@v4
- name: Download docs artifact
uses: actions/download-artifact@v4
with:
name: documentation
path: _html
- name: Publish to GitHub Pages
uses: crazy-max/ghaction-github-pages@v1
with:
target_branch: gh-pages
build_dir: _html
env:
GITHUB_TOKEN: ${{ github.token }}
================================================
FILE: .github/workflows/docker.yml
================================================
name: Docker
on:
push:
branches:
- main
pull_request:
branches:
- main
concurrency:
group: docker-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
build:
name: Build Docker image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
uses: docker/build-push-action@v6
with:
context: .
push: false
tags: server-reason-react:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
================================================
FILE: .gitignore
================================================
### OS ###
.DS_Store
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Dependency directories
node_modules/
# Build
**/dist/
# Optional npm cache directory
.npm
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
*.annot
*.cmo
*.cma
*.cmi
*.a
*.o
*.cmx
*.cmxs
*.cmxa
# dune working directory
_build/
# odoc (odoc-driver) html artifacts
_html/
# ocamlbuild targets
*.byte
*.native
# Merlin configuring file for Vim and Emacs
.merlin
# Dune generated files
*.install
# Local OPAM switch
_opam/
# vscode
.vscode
*.dump
_build
_opam
_esy
.bsb.lock
.merlin
.direnv/
demo/.running/*
================================================
FILE: .ocamlformat
================================================
profile=default
margin=120
version=0.28.1
================================================
FILE: CHANGES.md
================================================
# Changes
## 0.5.0
* Support Promise caching in react.client.components by @davesnx
* Reorder head content exactly like react-dom/server by @davesnx
* Implement hydration-compatible `useId` using React's tree-position-based algorithm, matching React 19 output. Adds `?identifier_prefix` to `renderToString`, `renderToStaticMarkup`, `renderToStream` and `render_html`. Fixes https://github.com/ml-in-barcelona/server-reason-react/issues/93
* Fix `renderToString` rendering Suspense children twice (once as trial, once with markers) due to side-effectful match expression. Children are now rendered into a separate buffer
* Change shape for React.Event.* since Js.t is now supported. All methods fail at runtime with `Runtime.fail_impossible_action_in_ssr`
* [server-reason-react.ppx] Strip units at any position (supporting mlx difference with [@JSX] transformations)
* Add runtime error with clear message when `React.cloneElement` is used with uppercase components by @davesnx
* Allow `[@platform js]` and `[@browser_only]` on externals to conditionally exclude them from native builds. Fixes https://github.com/ml-in-barcelona/server-reason-react/issues/170 by @davesnx
* Generate `makeProps` in the PPX by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/364
* Implement `Js.t` natively with `Js.Internal` and a type registry by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/363
* Add `React.useActionState` by @davesnx
* Add `key` into client components by @davesnx
* Fix several functions in Belt to match specification (`Belt.Array.setExn`, `Belt.Array.concat`, `Belt.MutableMap.remove`, `Belt.HashMap.keepMapInPlace`, and avoid double callback evaluation) by @yasunariw in https://github.com/ml-in-barcelona/server-reason-react/pull/362
* Implement `Belt.Array.getUndefined` and annotate `Belt.Array.push` as not implemented by @davesnx
* Remove deprecated folder from Belt and reorganise Belt tests by @davesnx
* Fix leaking `was_previous` when node was closing by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/361
* Fix `React.cloneElement` on `Static {}` components by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/359
* Require ppxlib >= 0.36 by @davesnx
## 0.4.1
* Use OCaml 5.4.0 by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/335
* Use latest ppxlib by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/334
* Update to latest quickjs by @davesnx
* Update dependency and usage by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/333
* Add filter to esbuild plugin to scope entrypoint by @pedrobslisboa in https://github.com/ml-in-barcelona/server-reason-react/pull/330
* Add back and forward navigation to nested router by @pedrobslisboa in https://github.com/ml-in-barcelona/server-reason-react/pull/329
* Implement memo and memoCustomCompareProps by @davesnx
* Move Date, BigInt and modularise Js by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/327
* Create complex navigation at RSC demo by @pedrobslisboa in https://github.com/ml-in-barcelona/server-reason-react/pull/307
## 0.4.0
* Add upper bound to quickjs 0.2.0
* Bump lwt to 5.9.2
* Expand styles prop into className and style props with optional handling by @pedrobslisboa in https://github.com/ml-in-barcelona/server-reason-react/pull/324
* Lowercase components have ?key:string by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/323
* Wrap client value on React.Upper_case_component by @pedrobslisboa in https://github.com/ml-in-barcelona/server-reason-react/pull/322
* Fix remove last element on nested_modules by @pedrobslisboa in https://github.com/ml-in-barcelona/server-reason-react/pull/321
* Add searchParams function to native URL by @pedrobslisboa in https://github.com/ml-in-barcelona/server-reason-react/pull/320
* Add URL construct function and improve lib build by @EmileTrotignon in https://github.com/ml-in-barcelona/server-reason-react/pull/317
* Specify model values at React by @pedrobslisboa in https://github.com/ml-in-barcelona/server-reason-react/pull/309
* Allow async in client props by @pedrobslisboa in https://github.com/ml-in-barcelona/server-reason-react/pull/315
* Improve the Fiber and Model stream context by @pedrobslisboa in https://github.com/ml-in-barcelona/server-reason-react/pull/312
* Align Suspense with reason-react by @pedrobslisboa in https://github.com/ml-in-barcelona/server-reason-react/pull/311
* Make client component to execute in runtime by @pedrobslisboa in https://github.com/ml-in-barcelona/server-reason-react/pull/306
* Fix mismatch of the model and html on render_html by @pedrobslisboa in https://github.com/ml-in-barcelona/server-reason-react/pull/305
* Fix createFromFetch interface and avoid transition on navigation by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/299
* Change ppx execution order (styles expansion in server-reason-react) by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/297
* Rename use function to usePromise in Experimental module by @pedrobslisboa in https://github.com/ml-in-barcelona/server-reason-react/pull/298
* Add shared-folder-prefix arg to ppx by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/294
## 0.3.1
* Update quickjs dependency to 0.1.2 by @davesnx
## 0.3.0
* browser-ppx: process stritems by @jchavarri in https://github.com/ml-in-barcelona/server-reason-react/pull/127
* Make React.Children.* APIs work as expected by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/130
* Improve global crashes by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/132
* Support assets in `mel.module` by @jchavarri in https://github.com/ml-in-barcelona/server-reason-react/pull/134
* browser_only: don't convert to runtime errors on identifiers or function application by @jchavarri in https://github.com/ml-in-barcelona/server-reason-react/pull/138
* Port `j` quoted strings interpolation from Melange by @jchavarri in https://github.com/ml-in-barcelona/server-reason-react/pull/139
* mel.module: handle asset prefix by @jchavarri in https://github.com/ml-in-barcelona/server-reason-react/pull/140
* Add browser_only transformation to useEffect automatically by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/145
* Append doctype tag on html lowercase by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/136
* Transform Pexp_function with browser_only by @davesnx in https://github.com/ml-in-barcelona/server-reason-react/pull/146
## 0.2.0
- Remove data-reactroot attr from ReactDOM.renderToString #129 by @pedrobslisboa
- Make useUrl return the provided serverUrl #125 by @purefunctor
- Replace Js.Re implemenation from `pcre` to quickjs b1a3e225cdad1298d705fbbd9618e15b0427ef0f by @davesnx
- Remove Belt.Array.push #122 by @davesnx
## 0.1.0
Initial release of server-reason-react, includes:
- Server-side rendering of ReasonReact components (renderToString, renderToStaticMarkup & renderToLwtStream)
- `server-reason-react.browser_ppx` for skipping code from the server
- `server-reason-react.melange_ppx` for enabling melange bindings and extensions which run on the server
- `server-reason-react.belt` a native Belt implementation
- `server-reason-react.js` a native Js implementation (unsafe and limited)
- `server-reason-react.url` and `server-reason-react.url-native` a universal library with both implementations to work with URLs on the server and the client
- `server-reason-react.promise` and `server-reason-react.promise-native` a universal library with both implementations to work with Promises on the server and the client. Based on https://github.com/aantron/promise
- `server-reason-react.melange-fetch` a fork of melange-fetch which is a melange library to fetch data on the client via the Fetch API. This fork is to be able to compile it on the server (not running).
- `server-reason-react.webapi` a fork of melange-webapi which is a melange library to work with the Web API on the client. This fork is to be able to compile it on the server (not running).
================================================
FILE: Dockerfile
================================================
FROM ocaml/opam:ubuntu-22.04-ocaml-5.4 AS builder
RUN sudo apt-get update && sudo apt-get install -y --no-install-recommends curl git libev-dev libssl-dev && \
sudo apt-get remove -y nodejs npm && sudo apt-get autoremove -y
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && \
sudo apt-get update && \
sudo apt-get install -y --no-install-recommends nodejs && \
sudo npm install -g npm@latest
RUN sudo ln -sf /usr/bin/opam-2.5 /usr/bin/opam && opam init --reinit -n
WORKDIR /app
RUN opam remote set-url default https://opam.ocaml.org
RUN cd ~/opam-repository && git fetch -q origin master && git reset --hard origin/master && opam update -y
COPY Makefile ./
COPY *.opam ./
COPY *.opam.template ./
COPY dune ./
COPY dune-project ./
RUN make pin
RUN opam update -y && opam install . --deps-only -y && opam install dream -y
WORKDIR /app/demo
COPY demo/package.json ./package.json
COPY demo/package-lock.json ./package-lock.json
RUN sudo npm ci --omit=dev
WORKDIR /app/demo/client
COPY demo/client/package.json ./package.json
COPY demo/client/package-lock.json ./package-lock.json
RUN sudo npm install
WORKDIR /app
COPY . .
RUN sudo chown -R opam:opam /app && opam exec -- dune build @demo --profile=dev
RUN opam clean -a -c -s --logs && rm -rf /home/opam/opam-repository
FROM ocaml/opam:ubuntu-22.04-ocaml-5.4
RUN sudo apt-get update && sudo apt-get install -y --no-install-recommends libev4 libssl3 && \
sudo rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder --chown=opam:opam /home/opam/.opam /home/opam/.opam
COPY --from=builder --chown=opam:opam /app /app
ENV PATH="/home/opam/.opam/5.4/bin:${PATH}"
EXPOSE 8080
CMD ["opam", "exec", "--switch", "5.4", "--", "_build/default/demo/server/server.exe"]
================================================
FILE: LICENSE.md
================================================
Copyright 2024 David Sancho & Javier Chavarri (ml-in-barcelona team)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: Makefile
================================================
project_name = server-reason-react
DUNE = opam exec -- dune
opam_file = $(project_name).opam
.PHONY: help
help: ## Print this help message
@echo "";
@echo "List of available make commands";
@echo "";
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}';
@echo "";
.PHONY: build
build: ## Build the project, including non installable libraries and executables
$(DUNE) build --profile=dev
.PHONY: build-prod
build-prod: ## Build for production (--profile=prod)
$(DUNE) build --profile=prod
.PHONY: dev
dev: ## Build in watch mode
$(DUNE) build -w --profile=dev
.PHONY: clean
clean: ## Clean artifacts
$(DUNE) clean
.PHONY: test
test: ## Run the unit tests
$(DUNE) build @runtest
.PHONY: test-watch
test-watch: ## Run the unit tests in watch mode
$(DUNE) build @runtest -w
.PHONY: test-promote
test-promote: ## Updates snapshots and promotes it to correct
$(DUNE) build @runtest --auto-promote
.PHONY: format
format: ## Format the codebase with ocamlformat
@DUNE_CONFIG__GLOBAL_LOCK=disabled $(DUNE) build @fmt --auto-promote
.PHONY: format-check
format-check: ## Checks if format is correct
@DUNE_CONFIG__GLOBAL_LOCK=disabled $(DUNE) build @fmt
.PHONY: init
setup-githooks: ## Setup githooks
git config core.hooksPath .githooks
.PHONY: create-switch
create-switch: ## Create opam switch
opam switch create . 5.4.0 --deps-only --with-test -y
.PHONY: install
install:
opam install . --deps-only --with-test --with-doc --with-dev-setup -y
.PHONY: install-npm
install-npm:
cd packages/esbuild-plugin && npm install;
cd packages/react-server-dom-esbuild && npm install;
cd demo && npm install;
cd demo/client && npm install
.PHONY: pin
pin: ## Pin dependencies
echo "No pins needed"
.PHONY: init
init: setup-githooks create-switch pin install install-npm ## Create a local dev enviroment
.PHONY: ppx-test
ppx-test: ## Run ppx tests
$(DUNE) runtest packages/server-reason-react-ppx
.PHONY: ppx-test-watch
ppx-test-watch: ## Run ppx tests in watch mode
$(DUNE) runtest packages/server-reason-react-ppx --watch
.PHONY: ppx-test-promote
ppx-test-promote: ## Prommote ppx tests snapshots
$(DUNE) runtest packages/server-reason-react-ppx --auto-promote
.PHONY: lib-test
lib-test: ## Run library tests
$(DUNE) exec test/test.exe
.PHONY: demo-build
demo-build: ## Build the project (client, server and universal)
$(DUNE) build --profile=dev @demo
.PHONY: demo-build-watch
demo-build-watch: ## Watch demo (client, server and universal)
$(DUNE) build --profile=dev @demo --force --watch
.PHONY: demo
demo-serve: demo-build ## Serve the demo executable
@opam exec -- _build/default/demo/server/server.exe
.PHONY: demo-serve-watch
demo-serve-watch: ## Run demo executable on watch mode (listening to built_at.txt changes)
@watchexec --no-vcs-ignore -w demo/.running/built_at.txt -r -c -- "_build/default/demo/server/server.exe"
.PHONY: subst
subst: ## Run dune substitute
$(DUNE) subst
.PHONY: docs
docs: ## Generate odoc documentation
$(DUNE) build @install
$(DUNE) exec -- odoc_driver server-reason-react --remap
# Because if the hack above, we can't have watch mode
.PHONY: docs-watch
docs-watch: ## Generate odoc docs
$(DUNE) build --root . -w @doc-new --profile=prod
.PHONY: docs-open
docs-open: ## Open odoc docs with default web browser
# open _build/default/_doc_new/html/docs/local/server-reason-react/index.html
open _html/server-reason-react/index.html
.PHONY: docs-serve
docs-serve: docs docs-open ## Open odoc docs with default web browser
.PHONY: build-bench
build-bench: ## Build benchmark executables
$(DUNE) build --profile=release benchmark/bench.exe
.PHONY: bench
bench: build-bench ## Run benchmarks
@$(DUNE) exec benchmark/bench.exe --profile=release --display-separate-messages --no-print-directory
.PHONY: bench-json
bench-json: build-bench ## Run benchmarks with JSON output for CI
@$(DUNE) exec benchmark/bench.exe --profile=release --display-separate-messages --no-print-directory -- --json > bench_results.json
.PHONY: bench-watch
bench-watch: build-bench ## Run benchmark in watch mode
@$(DUNE) exec benchmark/bench.exe --profile=release --display-separate-messages --no-print-directory --watch
.PHONY: bench-allocation
bench-allocation: ## Run allocation analysis
@$(DUNE) exec benchmark/allocation.exe --profile=release
container_name = server-reason-react-demo
current_hash = $(shell git rev-parse HEAD | cut -c1-7)
.PHONY: docker-build
docker-build: ## docker build
DOCKER_BUILDKIT=0 docker build . --tag "$(container_name):$(current_hash)" --platform linux/amd64
.PHONY: docker-run
docker-run: ## docker run
@docker run -d --platform linux/amd64 $(container_name):$(current_hash)
================================================
FILE: README.md
================================================
# server-reason-react
Native implementation of React's server-side rendering (SSR) and React Server Components (RSC) architecture for Reason.
Designed to be used with [reason-react](https://github.com/reasonml/reason-react) and [Melange](https://github.com/melange-re/melange). Together it enables developers to write efficient React components using a single language, while target both native executable and JavaScript.
## Features
- **Server-side rendering HTML** with `ReactDOM.renderToString`/`ReactDOM.renderToStaticMarkup`
- Server-side rendering **streaming HTML** with `ReactDOM.renderToStream` (similar to react@18 `renderToReadableStream`)
- Includes **`React.Suspense`** and **`React.use()`** implementations
- **server-reason-react-ppx** - A ppx transformation to support JSX on native
- All [reason-react](https://reasonml.github.io/reason-react/) interface is either implemented or stubbed (some of the methods, like React.useState need to be stubbed because they aren't used on the server!)
- **React Server Components** - A ReactServerDOM module for streaming RSC payload, an esbuild plugin to enhance the bundle with client-components mappings, a Dream middleware to serve the RSC endpoint and a dummy implementation of a router (still [work in progress](https://github.com/ml-in-barcelona/server-reason-react/issues/204))
> Warning: This repo contains a few parts that are considered experimental and there's no guarantee of stability. Most of the stable parts are used in production at ahrefs.com, app.ahrefs.com and wordcount.com. Check each module's documentation for more details.
## Why
There are plenty of motives for it, the main one is that [ahrefs](https://ahrefs.com) (the company I work for) needs it. We use OCaml for the backend and Reason (with React) for the frontend. We wanted to take advantage of the same features from React.js in the server as well.
Currently 100% of the public site ([ahrefs.com](https://ahrefs.com)), the shell part of the dashboard ([app.ahrefs.com](https://app.ahrefs.com)) and [wordcount.com](https://wordcount.com) are rendered on the server with `server-reason-react`.
What made us create this library was mostly:
- Use the same language (Reason) for both server and client
- Embrace server-client integrations such as type-safe routing, JSON decoding/encoding, sharing types and logic, while keep enjoying functional programming patterns
- Native performance is better than JavaScript performance (Node.js, Bun, Deno)
- Writing React from a different language than JavaScript, but still using the brilliant pieces from the ecosystem
- Exploration of OCaml effects and React
- Further exploration with OCaml multicore, direct-style and concurrency with React features such as async components, React.use or Suspense
Explained more about the motivation in [this blog post](https://sancho.dev/blog/server-side-rendering-react-in-ocaml) and also in my talk about [**Universal react in OCaml** at fun-ocaml 2024](https://www.youtube.com/watch?v=Oy3lZl2kE-0&t=92s&ab_channel=FUNOCaml) and [**Server side rendering React natively with Reason** at ReactAlicante 2023](https://www.youtube.com/watch?v=e3qY-Eg9zRY&ab_channel=ReactAlicante)
## Other libraries inside this repo
Aside from the core (`React`, `ReactDOM` and `ReactServerDOM`), server-reason-react repo contains some common melange libraries to ensure components are universal. Some of them are reimplementations in native of those libraries, and others are new implementations. Currently they are part of the repository, but eventually will be moved out to their own opam packages and repositories.
| Name | Description | Melange equivalent library |
|---------|-------------|---------|
| [`server-reason-react.browser_ppx`](https://ml-in-barcelona.github.io/server-reason-react/server-reason-react/browser_only.html) | A ppx to discard code for each platform with different attributes: `let%browser_only`, `switch%platform` and `@platform` |
| [`server-reason-react.url_js` and `server-reason-react.url_native`](https://ml-in-barcelona.github.io/server-reason-react/server-reason-react/server-reason-react.url_native/URL/index.html) | Universal URL module: binds to `window.URL` in browser, implemented with [`opam-uri`](https://github.com/mirage/ocaml-uri) in native |
| [`server-reason-react.melange_ppx`](https://ml-in-barcelona.github.io/server-reason-react/server-reason-react/externals-melange-attributes.html) | A ppx to add the melange attributes to native code | [melange.ppx](https://melange.re/v4.0.0/) |
| `server-reason-react.promise` | Vendored version of [aantron/promise](https://github.com/aantron/promise) with melange support [PR#80](https://github.com/aantron/promise/pull/80) | [promise](https://github.com/aantron/promise) |
| `server-reason-react.belt` | Implementation of Belt for native [API reference](https://ml-in-barcelona.github.io/server-reason-react/server-reason-react/server-reason-react.belt_native/Belt/index.html) | [melange.belt](https://melange.re/v4.0.0/api/ml/melange/Belt) |
| `server-reason-react.js` | Implementation of `Js` library for native (unsafe/incomplete). Check the issue [#110](https://github.com/ml-in-barcelona/server-reason-react/issues/110) for more details | [melange.js](https://melange.re/v4.0.0/api/ml/melange/Js) |
| `server-reason-react.fetch` | Stub of fetch with browser_ppx to compile in native | [melange.fetch](https://github.com/melange-community/melange-fetch) |
| `server-reason-react.webapi` | Stub version of Webapi library for native code compilation | [melange-webapi](https://github.com/melange-community/melange-webapi) |
| `server-reason-react.dom` | Stub version of Dom library for native code compilation | [melange-dom](https://melange.re/v4.0.0/) |
## [Documentation](https://ml-in-barcelona.github.io/server-reason-react/server-reason-react/index.html)
The [documentation site](https://ml-in-barcelona.github.io/server-reason-react/server-reason-react/index.html) is generated with odoc and hosted on GitHub Pages.
## Demo
The `demo` folder contains a bunch of demos under a server to showcases the usages of `server-reason-react`. Check the [README](demo/README.md) for how to setup and run it.
## Want to contribute?
[DM me](https://x.com/davesnx)
================================================
FILE: arch/browser/.gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
================================================
FILE: arch/browser/package.json
================================================
{
"name": "cra",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.6.4",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^13.5.0",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
================================================
FILE: arch/browser/public/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
================================================
FILE: arch/browser/public/manifest.json
================================================
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
================================================
FILE: arch/browser/src/index.css
================================================
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
================================================
FILE: arch/browser/src/index.js
================================================
import React from 'react';
import ReactDOM from 'react-dom/client';
const App = () => {
return (
<html>
<head>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.min.css"
/></head>
<main>
<div className="container">
<button className="btn">PRESS!</button>
</div>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.min.css"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.min.css"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.min.css"
/>
</main>
</html>
);
};
const root = ReactDOM.createRoot(window.document);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
================================================
FILE: arch/server/head-ordering.js
================================================
/**
* Reference file for React 19.1's head element ordering behavior.
* Run: node head-ordering.js (from arch/server/)
*
* React's server renderer reorders <head> children by priority bucket:
* 1. meta[charset]
* 2. meta[name="viewport"]
* 3. link[rel="stylesheet"][precedence] (stylesheet resources)
* 4. script[async][src] (async external scripts)
* 5. everything else (title, regular meta, regular link, plain style, etc.)
*
* Within each bucket, elements maintain their relative discovery order.
* Hoisted body elements appear before head-native elements within the same bucket.
*
* The RSC model keeps authored order; only the HTML shell is reordered.
* React 19's hydration tolerates this mismatch via HostSingleton handling for <head>.
*/
const React = require("react");
const { renderToPipeableStream } = require("react-dom/server");
const { Writable } = require("node:stream");
const el = React.createElement;
function render(element, label) {
return new Promise((resolve) => {
let out = "";
const writable = new Writable({
write(chunk, _, cb) {
out += chunk.toString();
cb();
},
});
const { pipe } = renderToPipeableStream(element, {
onAllReady() {
pipe(writable);
},
onError(err) {
console.error(label, err);
process.exitCode = 1;
},
});
writable.on("finish", () => {
console.log(`\n=== ${label} ===`);
console.log(out);
resolve();
});
});
}
async function main() {
// Case 1: Issue #303 exact sample
// Input order: style, link[precedence], meta[charset], meta[viewport]
// Output order: meta[charset], meta[viewport], link[precedence], style
await render(
el(
"html",
{ lang: "en" },
el(
"head",
null,
el("style", null),
el("link", { precedence: "low", rel: "stylesheet", href: "/foo.css" }),
el("meta", { charSet: "utf-8" }),
el("meta", { name: "viewport" }),
),
el("body", null),
),
"Case 1: Issue #303 sample",
);
// Case 2: Mixed explicit <head> + hoisted elements from <body>
await render(
el(
"html",
null,
el(
"head",
null,
el("title", null, "My Page"),
el("style", null, "body{margin:0}"),
el("link", {
rel: "stylesheet",
href: "/a.css",
precedence: "default",
}),
el("meta", { charSet: "utf-8" }),
),
el(
"body",
null,
el("link", {
rel: "stylesheet",
href: "/b.css",
precedence: "high",
}),
el("meta", { name: "viewport", content: "width=device-width" }),
el("title", null, "Override Title"),
),
),
"Case 2: Mixed head + body hoistables",
);
// Case 3: Broader bucket coverage
await render(
el(
"html",
null,
el(
"head",
null,
el("title", null, "App"),
el("script", { async: true, src: "/app.js" }),
el("link", {
rel: "stylesheet",
href: "/main.css",
precedence: "default",
}),
el("meta", { name: "viewport", content: "width=device-width" }),
el("meta", { charSet: "utf-8" }),
el("link", { rel: "preconnect", href: "https://cdn.example.com" }),
el("link", {
rel: "stylesheet",
href: "/theme.css",
precedence: "low",
}),
el("style", null, ".app{color:red}"),
el("meta", { name: "description", content: "A test app" }),
),
el("body", null),
),
"Case 3: Broad bucket coverage",
);
}
main();
================================================
FILE: arch/server/package.json
================================================
{
"name": "app",
"version": "0.0.1",
"scripts": {
"react-dom-server": "bun react-dom-server.js",
"render-html-to-stream": "bun render-html-to-stream.js",
"render-rsc-to-stream": "bun --conditions react-server render-rsc-to-stream.js"
},
"license": "MIT",
"dependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-server-dom-webpack": "^19.1.0"
}
}
================================================
FILE: arch/server/react-dom-server-node-dom-props.js
================================================
var properties = {}; // These props are reserved by React. They shouldn't be written to the DOM.
var reservedProps = [
"children",
"dangerouslySetInnerHTML", // TODO: This prevents the assignment of defaultValue to regular
// elements (not just inputs). Now that ReactDOMInput assigns to the
// defaultValue property -- do we need this?
"defaultValue",
"defaultChecked",
"innerHTML",
"suppressContentEditableWarning",
"suppressHydrationWarning",
"style",
];
reservedProps.forEach(function (name) {
properties[name] = new PropertyInfoRecord(
name,
RESERVED,
false, // mustUseProperty
name, // attributeName
null, // attributeNamespace
false
);
}); // A few React string attributes have a different name.
// This is a mapping from React prop names to the attribute names.
[
["acceptCharset", "accept-charset"],
["className", "class"],
["htmlFor", "for"],
["httpEquiv", "http-equiv"],
].forEach(function (_ref) {
var name = _ref[0],
attributeName = _ref[1];
properties[name] = new PropertyInfoRecord(
name,
STRING,
false, // mustUseProperty
attributeName, // attributeName
null, // attributeNamespace
false
);
}); // These are "enumerated" HTML attributes that accept "true" and "false".
// In React, we let users pass `true` and `false` even though technically
// these aren't boolean attributes (they are coerced to strings).
["contentEditable", "draggable", "spellCheck", "value"].forEach(function (
name
) {
properties[name] = new PropertyInfoRecord(
name,
BOOLEANISH_STRING,
false, // mustUseProperty
name.toLowerCase(), // attributeName
null, // attributeNamespace
false
);
}); // These are "enumerated" SVG attributes that accept "true" and "false".
// In React, we let users pass `true` and `false` even though technically
// these aren't boolean attributes (they are coerced to strings).
// Since these are SVG attributes, their attribute names are case-sensitive.
[
"autoReverse",
"externalResourcesRequired",
"focusable",
"preserveAlpha",
].forEach(function (name) {
properties[name] = new PropertyInfoRecord(
name,
BOOLEANISH_STRING,
false, // mustUseProperty
name, // attributeName
null, // attributeNamespace
false
);
}); // These are HTML boolean attributes.
[
"allowFullScreen",
"async", // Note: there is a special case that prevents it from being written to the DOM
// on the client side because the browsers are inconsistent. Instead we call focus().
"autoFocus",
"autoPlay",
"controls",
"default",
"defer",
"disabled",
"disablePictureInPicture",
"formNoValidate",
"hidden",
"loop",
"noModule",
"noValidate",
"open",
"playsInline",
"readOnly",
"required",
"reversed",
"scoped",
"seamless", // Microdata
"itemScope",
].forEach(function (name) {
properties[name] = new PropertyInfoRecord(
name,
BOOLEAN,
false, // mustUseProperty
name.toLowerCase(), // attributeName
null, // attributeNamespace
false
);
}); // These are the few React props that we set as DOM properties
// rather than attributes. These are all booleans.
[
"checked", // Note: `option.selected` is not updated if `select.multiple` is
// disabled with `removeAttribute`. We have special logic for handling this.
"multiple",
"muted",
"selected", // NOTE: if you add a camelCased prop to this list,
// you'll need to set attributeName to name.toLowerCase()
// instead in the assignment below.
].forEach(function (name) {
properties[name] = new PropertyInfoRecord(
name,
BOOLEAN,
true, // mustUseProperty
name, // attributeName
null, // attributeNamespace
false
);
}); // These are HTML attributes that are "overloaded booleans": they behave like
// booleans, but can also accept a string value.
[
"capture",
"download", // NOTE: if you add a camelCased prop to this list,
// you'll need to set attributeName to name.toLowerCase()
// instead in the assignment below.
].forEach(function (name) {
properties[name] = new PropertyInfoRecord(
name,
OVERLOADED_BOOLEAN,
false, // mustUseProperty
name, // attributeName
null, // attributeNamespace
false
);
}); // These are HTML attributes that must be positive numbers.
[
"cols",
"rows",
"size",
"span", // NOTE: if you add a camelCased prop to this list,
// you'll need to set attributeName to name.toLowerCase()
// instead in the assignment below.
].forEach(function (name) {
properties[name] = new PropertyInfoRecord(
name,
POSITIVE_NUMERIC,
false, // mustUseProperty
name, // attributeName
null, // attributeNamespace
false
);
}); // These are HTML attributes that must be numbers.
["rowSpan", "start"].forEach(function (name) {
properties[name] = new PropertyInfoRecord(
name,
NUMERIC,
false, // mustUseProperty
name.toLowerCase(), // attributeName
null, // attributeNamespace
false
);
});
var CAMELIZE = /[\-\:]([a-z])/g;
var capitalize = function (token) {
return token[1].toUpperCase();
}; // This is a list of all SVG attributes that need special casing, namespacing,
// or boolean value assignment. Regular attributes that just accept strings
// and have the same names are omitted, just like in the HTML whitelist.
// Some of these attributes can be hard to find. This list was created by
// scraping the MDN documentation.
[
"accent-height",
"alignment-baseline",
"arabic-form",
"baseline-shift",
"cap-height",
"clip-path",
"clip-rule",
"color-interpolation",
"color-interpolation-filters",
"color-profile",
"color-rendering",
"dominant-baseline",
"enable-background",
"fill-opacity",
"fill-rule",
"flood-color",
"flood-opacity",
"font-family",
"font-size",
"font-size-adjust",
"font-stretch",
"font-style",
"font-variant",
"font-weight",
"glyph-name",
"glyph-orientation-horizontal",
"glyph-orientation-vertical",
"horiz-adv-x",
"horiz-origin-x",
"image-rendering",
"letter-spacing",
"lighting-color",
"marker-end",
"marker-mid",
"marker-start",
"overline-position",
"overline-thickness",
"paint-order",
"panose-1",
"pointer-events",
"rendering-intent",
"shape-rendering",
"stop-color",
"stop-opacity",
"strikethrough-position",
"strikethrough-thickness",
"stroke-dasharray",
"stroke-dashoffset",
"stroke-linecap",
"stroke-linejoin",
"stroke-miterlimit",
"stroke-opacity",
"stroke-width",
"text-anchor",
"text-decoration",
"text-rendering",
"underline-position",
"underline-thickness",
"unicode-bidi",
"unicode-range",
"units-per-em",
"v-alphabetic",
"v-hanging",
"v-ideographic",
"v-mathematical",
"vector-effect",
"vert-adv-y",
"vert-origin-x",
"vert-origin-y",
"word-spacing",
"writing-mode",
"xmlns:xlink",
"x-height", // NOTE: if you add a camelCased prop to this list,
// you'll need to set attributeName to name.toLowerCase()
// instead in the assignment below.
].forEach(function (attributeName) {
var name = attributeName.replace(CAMELIZE, capitalize);
properties[name] = new PropertyInfoRecord(
name,
STRING,
false, // mustUseProperty
attributeName,
null, // attributeNamespace
false
);
}); // String SVG attributes with the xlink namespace.
[
"xlink:actuate",
"xlink:arcrole",
"xlink:role",
"xlink:show",
"xlink:title",
"xlink:type", // NOTE: if you add a camelCased prop to this list,
// you'll need to set attributeName to name.toLowerCase()
// instead in the assignment below.
].forEach(function (attributeName) {
var name = attributeName.replace(CAMELIZE, capitalize);
properties[name] = new PropertyInfoRecord(
name,
STRING,
false, // mustUseProperty
attributeName,
"http://www.w3.org/1999/xlink",
false
);
}); // String SVG attributes with the xml namespace.
[
"xml:base",
"xml:lang",
"xml:space", // NOTE: if you add a camelCased prop to this list,
// you'll need to set attributeName to name.toLowerCase()
// instead in the assignment below.
].forEach(function (attributeName) {
var name = attributeName.replace(CAMELIZE, capitalize);
properties[name] = new PropertyInfoRecord(
name,
STRING,
false, // mustUseProperty
attributeName,
"http://www.w3.org/XML/1998/namespace",
false
);
}); // These attribute exists both in HTML and SVG.
// The attribute name is case-sensitive in SVG so we can't just use
// the React name like we do for attributes that exist only in HTML.
["tabIndex", "crossOrigin"].forEach(function (attributeName) {
properties[attributeName] = new PropertyInfoRecord(
attributeName,
STRING,
false, // mustUseProperty
attributeName.toLowerCase(), // attributeName
null, // attributeNamespace
false
);
}); // These attributes accept URLs. These must not allow javascript: URLS.
// These will also need to accept Trusted Types object in the future.
var xlinkHref = "xlinkHref";
properties[xlinkHref] = new PropertyInfoRecord(
"xlinkHref",
STRING,
false, // mustUseProperty
"xlink:href",
"http://www.w3.org/1999/xlink",
true
);
["src", "href", "action", "formAction"].forEach(function (attributeName) {
properties[attributeName] = new PropertyInfoRecord(
attributeName,
STRING,
false, // mustUseProperty
attributeName.toLowerCase(), // attributeName
null, // attributeNamespace
true
);
});
/* */
// When adding attributes to the HTML or SVG whitelist, be sure to
// also add them to this module to ensure casing and incorrect name
// warnings.
var possibleStandardNames = {
// HTML
accept: "accept",
acceptcharset: "acceptCharset",
"accept-charset": "acceptCharset",
accesskey: "accessKey",
action: "action",
allowfullscreen: "allowFullScreen",
alt: "alt",
as: "as",
async: "async",
autocapitalize: "autoCapitalize",
autocomplete: "autoComplete",
autocorrect: "autoCorrect",
autofocus: "autoFocus",
autoplay: "autoPlay",
autosave: "autoSave",
capture: "capture",
cellpadding: "cellPadding",
cellspacing: "cellSpacing",
challenge: "challenge",
charset: "charSet",
checked: "checked",
children: "children",
cite: "cite",
class: "className",
classid: "classID",
classname: "className",
cols: "cols",
colspan: "colSpan",
content: "content",
contenteditable: "contentEditable",
contextmenu: "contextMenu",
controls: "controls",
controlslist: "controlsList",
coords: "coords",
crossorigin: "crossOrigin",
dangerouslysetinnerhtml: "dangerouslySetInnerHTML",
data: "data",
datetime: "dateTime",
default: "default",
defaultchecked: "defaultChecked",
defaultvalue: "defaultValue",
defer: "defer",
dir: "dir",
disabled: "disabled",
disablepictureinpicture: "disablePictureInPicture",
download: "download",
draggable: "draggable",
enctype: "encType",
for: "htmlFor",
form: "form",
formmethod: "formMethod",
formaction: "formAction",
formenctype: "formEncType",
formnovalidate: "formNoValidate",
formtarget: "formTarget",
frameborder: "frameBorder",
headers: "headers",
height: "height",
hidden: "hidden",
high: "high",
href: "href",
hreflang: "hrefLang",
htmlfor: "htmlFor",
httpequiv: "httpEquiv",
"http-equiv": "httpEquiv",
icon: "icon",
id: "id",
innerhtml: "innerHTML",
inputmode: "inputMode",
integrity: "integrity",
is: "is",
itemid: "itemID",
itemprop: "itemProp",
itemref: "itemRef",
itemscope: "itemScope",
itemtype: "itemType",
keyparams: "keyParams",
keytype: "keyType",
kind: "kind",
label: "label",
lang: "lang",
list: "list",
loop: "loop",
low: "low",
manifest: "manifest",
marginwidth: "marginWidth",
marginheight: "marginHeight",
max: "max",
maxlength: "maxLength",
media: "media",
mediagroup: "mediaGroup",
method: "method",
min: "min",
minlength: "minLength",
multiple: "multiple",
muted: "muted",
name: "name",
nomodule: "noModule",
nonce: "nonce",
novalidate: "noValidate",
open: "open",
optimum: "optimum",
pattern: "pattern",
placeholder: "placeholder",
playsinline: "playsInline",
poster: "poster",
preload: "preload",
profile: "profile",
radiogroup: "radioGroup",
readonly: "readOnly",
referrerpolicy: "referrerPolicy",
rel: "rel",
required: "required",
reversed: "reversed",
role: "role",
rows: "rows",
rowspan: "rowSpan",
sandbox: "sandbox",
scope: "scope",
scoped: "scoped",
scrolling: "scrolling",
seamless: "seamless",
selected: "selected",
shape: "shape",
size: "size",
sizes: "sizes",
span: "span",
spellcheck: "spellCheck",
src: "src",
srcdoc: "srcDoc",
srclang: "srcLang",
srcset: "srcSet",
start: "start",
step: "step",
style: "style",
summary: "summary",
tabindex: "tabIndex",
target: "target",
title: "title",
type: "type",
usemap: "useMap",
value: "value",
width: "width",
wmode: "wmode",
wrap: "wrap",
// SVG
about: "about",
accentheight: "accentHeight",
"accent-height": "accentHeight",
accumulate: "accumulate",
additive: "additive",
alignmentbaseline: "alignmentBaseline",
"alignment-baseline": "alignmentBaseline",
allowreorder: "allowReorder",
alphabetic: "alphabetic",
amplitude: "amplitude",
arabicform: "arabicForm",
"arabic-form": "arabicForm",
ascent: "ascent",
attributename: "attributeName",
attributetype: "attributeType",
autoreverse: "autoReverse",
azimuth: "azimuth",
basefrequency: "baseFrequency",
baselineshift: "baselineShift",
"baseline-shift": "baselineShift",
baseprofile: "baseProfile",
bbox: "bbox",
begin: "begin",
bias: "bias",
by: "by",
calcmode: "calcMode",
capheight: "capHeight",
"cap-height": "capHeight",
clip: "clip",
clippath: "clipPath",
"clip-path": "clipPath",
clippathunits: "clipPathUnits",
cliprule: "clipRule",
"clip-rule": "clipRule",
color: "color",
colorinterpolation: "colorInterpolation",
"color-interpolation": "colorInterpolation",
colorinterpolationfilters: "colorInterpolationFilters",
"color-interpolation-filters": "colorInterpolationFilters",
colorprofile: "colorProfile",
"color-profile": "colorProfile",
colorrendering: "colorRendering",
"color-rendering": "colorRendering",
contentscripttype: "contentScriptType",
contentstyletype: "contentStyleType",
cursor: "cursor",
cx: "cx",
cy: "cy",
d: "d",
datatype: "datatype",
decelerate: "decelerate",
descent: "descent",
diffuseconstant: "diffuseConstant",
direction: "direction",
display: "display",
divisor: "divisor",
dominantbaseline: "dominantBaseline",
"dominant-baseline": "dominantBaseline",
dur: "dur",
dx: "dx",
dy: "dy",
edgemode: "edgeMode",
elevation: "elevation",
enablebackground: "enableBackground",
"enable-background": "enableBackground",
end: "end",
exponent: "exponent",
externalresourcesrequired: "externalResourcesRequired",
fill: "fill",
fillopacity: "fillOpacity",
"fill-opacity": "fillOpacity",
fillrule: "fillRule",
"fill-rule": "fillRule",
filter: "filter",
filterres: "filterRes",
filterunits: "filterUnits",
floodopacity: "floodOpacity",
"flood-opacity": "floodOpacity",
floodcolor: "floodColor",
"flood-color": "floodColor",
focusable: "focusable",
fontfamily: "fontFamily",
"font-family": "fontFamily",
fontsize: "fontSize",
"font-size": "fontSize",
fontsizeadjust: "fontSizeAdjust",
"font-size-adjust": "fontSizeAdjust",
fontstretch: "fontStretch",
"font-stretch": "fontStretch",
fontstyle: "fontStyle",
"font-style": "fontStyle",
fontvariant: "fontVariant",
"font-variant": "fontVariant",
fontweight: "fontWeight",
"font-weight": "fontWeight",
format: "format",
from: "from",
fx: "fx",
fy: "fy",
g1: "g1",
g2: "g2",
glyphname: "glyphName",
"glyph-name": "glyphName",
glyphorientationhorizontal: "glyphOrientationHorizontal",
"glyph-orientation-horizontal": "glyphOrientationHorizontal",
glyphorientationvertical: "glyphOrientationVertical",
"glyph-orientation-vertical": "glyphOrientationVertical",
glyphref: "glyphRef",
gradienttransform: "gradientTransform",
gradientunits: "gradientUnits",
hanging: "hanging",
horizadvx: "horizAdvX",
"horiz-adv-x": "horizAdvX",
horizoriginx: "horizOriginX",
"horiz-origin-x": "horizOriginX",
ideographic: "ideographic",
imagerendering: "imageRendering",
"image-rendering": "imageRendering",
in2: "in2",
in: "in",
inlist: "inlist",
intercept: "intercept",
k1: "k1",
k2: "k2",
k3: "k3",
k4: "k4",
k: "k",
kernelmatrix: "kernelMatrix",
kernelunitlength: "kernelUnitLength",
kerning: "kerning",
keypoints: "keyPoints",
keysplines: "keySplines",
keytimes: "keyTimes",
lengthadjust: "lengthAdjust",
letterspacing: "letterSpacing",
"letter-spacing": "letterSpacing",
lightingcolor: "lightingColor",
"lighting-color": "lightingColor",
limitingconeangle: "limitingConeAngle",
local: "local",
markerend: "markerEnd",
"marker-end": "markerEnd",
markerheight: "markerHeight",
markermid: "markerMid",
"marker-mid": "markerMid",
markerstart: "markerStart",
"marker-start": "markerStart",
markerunits: "markerUnits",
markerwidth: "markerWidth",
mask: "mask",
maskcontentunits: "maskContentUnits",
maskunits: "maskUnits",
mathematical: "mathematical",
mode: "mode",
numoctaves: "numOctaves",
offset: "offset",
opacity: "opacity",
operator: "operator",
order: "order",
orient: "orient",
orientation: "orientation",
origin: "origin",
overflow: "overflow",
overlineposition: "overlinePosition",
"overline-position": "overlinePosition",
overlinethickness: "overlineThickness",
"overline-thickness": "overlineThickness",
paintorder: "paintOrder",
"paint-order": "paintOrder",
panose1: "panose1",
"panose-1": "panose1",
pathlength: "pathLength",
patterncontentunits: "patternContentUnits",
patterntransform: "patternTransform",
patternunits: "patternUnits",
pointerevents: "pointerEvents",
"pointer-events": "pointerEvents",
points: "points",
pointsatx: "pointsAtX",
pointsaty: "pointsAtY",
pointsatz: "pointsAtZ",
prefix: "prefix",
preservealpha: "preserveAlpha",
preserveaspectratio: "preserveAspectRatio",
primitiveunits: "primitiveUnits",
property: "property",
r: "r",
radius: "radius",
refx: "refX",
refy: "refY",
renderingintent: "renderingIntent",
"rendering-intent": "renderingIntent",
repeatcount: "repeatCount",
repeatdur: "repeatDur",
requiredextensions: "requiredExtensions",
requiredfeatures: "requiredFeatures",
resource: "resource",
restart: "restart",
result: "result",
results: "results",
rotate: "rotate",
rx: "rx",
ry: "ry",
scale: "scale",
security: "security",
seed: "seed",
shaperendering: "shapeRendering",
"shape-rendering": "shapeRendering",
slope: "slope",
spacing: "spacing",
specularconstant: "specularConstant",
specularexponent: "specularExponent",
speed: "speed",
spreadmethod: "spreadMethod",
startoffset: "startOffset",
stddeviation: "stdDeviation",
stemh: "stemh",
stemv: "stemv",
stitchtiles: "stitchTiles",
stopcolor: "stopColor",
"stop-color": "stopColor",
stopopacity: "stopOpacity",
"stop-opacity": "stopOpacity",
strikethroughposition: "strikethroughPosition",
"strikethrough-position": "strikethroughPosition",
strikethroughthickness: "strikethroughThickness",
"strikethrough-thickness": "strikethroughThickness",
string: "string",
stroke: "stroke",
strokedasharray: "strokeDasharray",
"stroke-dasharray": "strokeDasharray",
strokedashoffset: "strokeDashoffset",
"stroke-dashoffset": "strokeDashoffset",
strokelinecap: "strokeLinecap",
"stroke-linecap": "strokeLinecap",
strokelinejoin: "strokeLinejoin",
"stroke-linejoin": "strokeLinejoin",
strokemiterlimit: "strokeMiterlimit",
"stroke-miterlimit": "strokeMiterlimit",
strokewidth: "strokeWidth",
"stroke-width": "strokeWidth",
strokeopacity: "strokeOpacity",
"stroke-opacity": "strokeOpacity",
suppresscontenteditablewarning: "suppressContentEditableWarning",
suppresshydrationwarning: "suppressHydrationWarning",
surfacescale: "surfaceScale",
systemlanguage: "systemLanguage",
tablevalues: "tableValues",
targetx: "targetX",
targety: "targetY",
textanchor: "textAnchor",
"text-anchor": "textAnchor",
textdecoration: "textDecoration",
"text-decoration": "textDecoration",
textlength: "textLength",
textrendering: "textRendering",
"text-rendering": "textRendering",
to: "to",
transform: "transform",
typeof: "typeof",
u1: "u1",
u2: "u2",
underlineposition: "underlinePosition",
"underline-position": "underlinePosition",
underlinethickness: "underlineThickness",
"underline-thickness": "underlineThickness",
unicode: "unicode",
unicodebidi: "unicodeBidi",
"unicode-bidi": "unicodeBidi",
unicoderange: "unicodeRange",
"unicode-range": "unicodeRange",
unitsperem: "unitsPerEm",
"units-per-em": "unitsPerEm",
unselectable: "unselectable",
valphabetic: "vAlphabetic",
"v-alphabetic": "vAlphabetic",
values: "values",
vectoreffect: "vectorEffect",
"vector-effect": "vectorEffect",
version: "version",
vertadvy: "vertAdvY",
"vert-adv-y": "vertAdvY",
vertoriginx: "vertOriginX",
"vert-origin-x": "vertOriginX",
vertoriginy: "vertOriginY",
"vert-origin-y": "vertOriginY",
vhanging: "vHanging",
"v-hanging": "vHanging",
videographic: "vIdeographic",
"v-ideographic": "vIdeographic",
viewbox: "viewBox",
viewtarget: "viewTarget",
visibility: "visibility",
vmathematical: "vMathematical",
"v-mathematical": "vMathematical",
vocab: "vocab",
widths: "widths",
wordspacing: "wordSpacing",
"word-spacing": "wordSpacing",
writingmode: "writingMode",
"writing-mode": "writingMode",
x1: "x1",
x2: "x2",
x: "x",
xchannelselector: "xChannelSelector",
xheight: "xHeight",
"x-height": "xHeight",
xlinkactuate: "xlinkActuate",
"xlink:actuate": "xlinkActuate",
xlinkarcrole: "xlinkArcrole",
"xlink:arcrole": "xlinkArcrole",
xlinkhref: "xlinkHref",
"xlink:href": "xlinkHref",
xlinkrole: "xlinkRole",
"xlink:role": "xlinkRole",
xlinkshow: "xlinkShow",
"xlink:show": "xlinkShow",
xlinktitle: "xlinkTitle",
"xlink:title": "xlinkTitle",
xlinktype: "xlinkType",
"xlink:type": "xlinkType",
xmlbase: "xmlBase",
"xml:base": "xmlBase",
xmllang: "xmlLang",
"xml:lang": "xmlLang",
xmlns: "xmlns",
"xml:space": "xmlSpace",
xmlnsxlink: "xmlnsXlink",
"xmlns:xlink": "xmlnsXlink",
xmlspace: "xmlSpace",
y1: "y1",
y2: "y2",
y: "y",
ychannelselector: "yChannelSelector",
z: "z",
zoomandpan: "zoomAndPan",
};
/* */
================================================
FILE: arch/server/react-dom-server.js
================================================
const React = require("react");
const ReactDOM = require("react-dom/server");
let first = React.createElement(
"div",
{ key: "fi", value1: "aaa", style: { margin: "1px" } },
["first"],
);
let clon = React.cloneElement(
first,
{ value: "bbb", more: 22, style: { margin: "1", padding: "0px" } },
["asdf"],
);
const { Provider, Consumer } = React.createContext(10);
var app = () => {
/* https://fb.me/react-uselayouteffect-ssr */
React.useLayoutEffect(() => {
console.log("asdfdsf");
return () => {
console.log("asdfsdf");
};
});
return React.createElement("div", { className: "contenido" }, []);
};
var app = () => {
let [state, setState] = React.useState(0);
React.useEffect(() => {
setState(state + 1);
console.log("asdfdsf");
return () => {
console.log("asdfsdf");
};
});
return React.createElement("div", { className: "contenido" }, []);
};
var app = () => {
let [state, setState] = React.useState(0);
let ref = React.useRef(true);
console.log(state);
if (ref.current) {
setState(state + 1);
ref.current = false;
}
React.useEffect(() => {
console.log("asfsdafsafsadf");
});
React.useEffect(() => {
console.log("asfsdafsafsadf");
}, [state]);
console.log(state);
return React.createElement("div", null, [state]);
};
/* var app = () => {
return React.createElement("div", {
dangerouslySetInnerHTML: { __html: "asdf" },
});
}; */
var ctx = React.createContext(10);
var context_user = () => {
let a = React.useContext(ctx);
console.log(a);
return React.createElement("div", { key: 1 }, [a]);
};
var app = () => {
let ref = React.useRef(333);
console.log(ref);
return React.createElement(
ctx.Provider,
{ value: 0, ref: ref },
React.createElement(context_user),
);
};
var app = React.forwardRef(() => {
let ref = React.useRef(333);
console.log(ref);
return React.createElement(
ctx.Provider,
{ value: 0, ref: ref },
React.createElement(context_user),
);
});
var app = () => {
return React.createElement(
"script",
{
"aria-hidden": "true",
},
`console.log("asdfas");`,
);
};
function App() {
let value = "asdfasdf";
return <input id="sidebar-search-input" placeholder="Search" value={value} />;
}
console.log(ReactDOM.renderToStaticMarkup(<App />));
================================================
FILE: arch/server/render-html-to-stream.js
================================================
import React from "react";
import * as ReactDOM from "react-dom/server";
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom'
const sleep = (seconds) =>
new Promise((res) => setTimeout(res, seconds * 1000));
const DeferredComponent = async ({ by, children }) => {
await sleep(by);
return (
<div>
Sleep {by}s, {children}
</div>
);
};
const decoder = new TextDecoder();
const debug = (readableStream) => {
const reader = readableStream.getReader();
const debugReader = ({ done, value }) => {
if (done) {
console.log("Stream complete");
return;
}
console.log(decoder.decode(value));
console.log(" ");
return reader.read().then(debugReader);
};
reader.read().then(debugReader);
};
/* const App = () => (
<React.Suspense fallback="Fallback 1">
<DeferredComponent by={1}>
<React.Suspense fallback="Fallback 2">
<DeferredComponent by={1}>"lol"</DeferredComponent>
</React.Suspense>
</DeferredComponent>
</React.Suspense>
); */
/* const App = () => (
<div>
<React.Suspense fallback="Fallback 1">
<DeferredComponent by={0}>"lol"</DeferredComponent>
</React.Suspense>
</div>
); */
/* const AlwaysThrow = () => {
throw new Error("always throwing");
};
const App = () => (
<React.Suspense fallback="Fallback 1">
<DeferredComponent by={1}>
<React.Suspense fallback="Fallback 2">
<AlwaysThrow/>
</React.Suspense>
</DeferredComponent>
</React.Suspense>
); */
/* function App() {
return React.createElement(
Suspense,
{ fallback: "Fallback 1" },
React.createElement(DeferredComponent,
{ by: 0.02 },
React.createElement(
Suspense,
{ fallback: "Fallback 2" },
React.createElement(DeferredComponent,
{ by: 0.02 },
"lol"
)
)
)
);
} */
/* function App() {
return (
<html>
<body>
<head>"asdf"</head>
<div key="33">lol</div>
</body>
</html>
);
}
*/
/* const AnotherComponent = async () => {
preinit('analytics.js', { as: 'script' });
await sleep(1);
return <><script async={true} src="analytics.js" />
<div>AnotherComponent</div></>;
}; */
const Component = () => {
return <div>
<link rel="stylesheet" precedence="low" href="https://cdn.com/main.css" />
<link rel="icon" href="favicon.ico" />
<link rel="pingback" href="http://www.example.com/xmlrpc.php" />
<style>
{"body { background-color: red; }"}
</style>
<script>
{"console.log('hello');"}
</script>
</div>
}
const App = () => {
return (
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" precedence="high" /></head>
<div className="container"></div>
</html >
)
};
ReactDOM.renderToReadableStream(<App />).then((stream) => {
debug(stream);
});
================================================
FILE: arch/server/render-rsc-to-stream.js
================================================
import React from "react";
import { renderToPipeableStream } from "react-server-dom-webpack/server";
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom'
const DefferedComponent = async ({ sleep, children }) => {
await new Promise((res) => setTimeout(() => res(), sleep * 1000));
return (
<span>
Sleep {sleep}s, {children}
</span>
);
};
const decoder = new TextDecoder();
const debug = (readableStream) => {
const reader = readableStream.getReader();
const debugReader = ({ done, value }) => {
if (done) {
console.log("Stream complete");
return;
}
console.log(decoder.decode(value));
console.log(" ");
return reader.read().then(debugReader);
};
reader.read().then(debugReader);
};
const sleep = (seconds) =>
new Promise((res) => setTimeout(res, seconds * 1000));
const reject = (_) =>
new Promise((_res, rej) => { return rej(0) });
const AlwaysError = () => {
throw new Error("lol");
};
let Await_tick = ({ num }) => {
return num
}
const App = () => {
return (
<div className="container">
<link rel="stylesheet" href="bootstrap.min.css" precedence="high" /></div>
)
};
const { pipe } = renderToPipeableStream(<App />);
pipe(process.stdout);
/* https://codesandbox.io/p/sandbox/vibrant-voice-hdrlzt?file=%2Fsrc%2FApp.js */
================================================
FILE: arch/server/test-useid-edge-cases.js
================================================
const React = require("react");
const ReactDOM = require("react-dom/server");
function DivWithId({ label }) {
const id = React.useId();
return React.createElement("div", { id, "data-label": label });
}
// ── Edge Case 1: useId inside Suspense children ──────────────────────
// (sync component, no actual suspension)
function SuspenseWithUseId() {
return React.createElement(
React.Suspense,
{ fallback: React.createElement("div", null, "loading") },
React.createElement(DivWithId, { label: "inside-suspense" }),
);
}
// ── Edge Case 2: useId in Suspense fallback vs children ──────────────
// Does Suspense fork the tree context?
function FallbackWithId() {
const id = React.useId();
return React.createElement("div", { id, "data-label": "fallback" });
}
function SuspenseWithIdInBoth() {
return React.createElement(
"div",
null,
React.createElement(
React.Suspense,
{
fallback: React.createElement(FallbackWithId),
},
React.createElement(DivWithId, { label: "content" }),
),
React.createElement(DivWithId, { label: "sibling" }),
);
}
// ── Edge Case 3: Fragment wrapping ───────────────────────────────────
// Does Fragment affect tree context?
function FragmentTest() {
return React.createElement(
"div",
null,
React.createElement(
React.Fragment,
null,
React.createElement(DivWithId, { label: "in-fragment" }),
),
);
}
// ── Edge Case 4: Fragment with multiple children ─────────────────────
function FragmentMultipleChildren() {
return React.createElement(
"div",
null,
React.createElement(
React.Fragment,
null,
React.createElement(DivWithId, { label: "frag-child-1" }),
React.createElement(DivWithId, { label: "frag-child-2" }),
),
);
}
// ── Edge Case 5: Nested fragments ────────────────────────────────────
function NestedFragments() {
return React.createElement(
"div",
null,
React.createElement(
React.Fragment,
null,
React.createElement(
React.Fragment,
null,
React.createElement(DivWithId, { label: "nested-frag" }),
),
),
);
}
// ── Edge Case 6: Empty elements between components ───────────────────
function NullBetween() {
return React.createElement(
"div",
null,
React.createElement(DivWithId, { label: "before-null" }),
null,
React.createElement(DivWithId, { label: "after-null" }),
);
}
// ── Edge Case 7: useId with conditional children ─────────────────────
function ConditionalChildren({ show }) {
return React.createElement(
"div",
null,
show ? React.createElement(DivWithId, { label: "conditional" }) : null,
React.createElement(DivWithId, { label: "always" }),
);
}
// ── Edge Case 8: useId with keyed fragments ──────────────────────────
function KeyedFragments() {
return React.createElement(
"div",
null,
React.createElement(
React.Fragment,
{ key: "a" },
React.createElement(DivWithId, { label: "keyed-a" }),
),
React.createElement(
React.Fragment,
{ key: "b" },
React.createElement(DivWithId, { label: "keyed-b" }),
),
);
}
// ── Edge Case 9: Deeply nested list (many siblings) ──────────────────
function ManySiblings() {
const items = Array.from({ length: 10 }, (_, i) =>
React.createElement(DivWithId, { key: String(i), label: `item-${i}` }),
);
return React.createElement("div", null, items);
}
// ── Edge Case 10: useId after Suspense boundary ─────────────────────
function UseIdAfterSuspense() {
return React.createElement(
"div",
null,
React.createElement(
React.Suspense,
{ fallback: React.createElement("div", null, "loading") },
React.createElement(DivWithId, { label: "inside" }),
),
React.createElement(DivWithId, { label: "after-suspense" }),
);
}
// ── Edge Case 11: Provider/Context with useId ────────────────────────
const MyContext = React.createContext("default");
function ProviderWithUseId() {
return React.createElement(
MyContext.Provider,
{ value: "provided" },
React.createElement(DivWithId, { label: "inside-provider" }),
);
}
// ── Edge Case 12: Mixed: Provider, Fragment, Suspense ────────────────
function KitchenSink() {
return React.createElement(
"div",
null,
React.createElement(
MyContext.Provider,
{ value: "a" },
React.createElement(
React.Fragment,
null,
React.createElement(DivWithId, { label: "provider-frag-1" }),
React.createElement(
React.Suspense,
{
fallback: React.createElement("span", null, "..."),
},
React.createElement(DivWithId, { label: "provider-suspense" }),
),
),
),
React.createElement(DivWithId, { label: "outside" }),
);
}
const tests = [
["Edge 1: useId inside Suspense (sync)", SuspenseWithUseId],
["Edge 2: Suspense with useId in content + sibling", SuspenseWithIdInBoth],
["Edge 3: Fragment wrapping (single child)", FragmentTest],
["Edge 4: Fragment with multiple children", FragmentMultipleChildren],
["Edge 5: Nested fragments", NestedFragments],
["Edge 6: Null between components", NullBetween],
["Edge 8: Keyed fragments", KeyedFragments],
["Edge 9: Many siblings (10)", ManySiblings],
["Edge 10: useId after Suspense", UseIdAfterSuspense],
["Edge 11: Provider with useId", ProviderWithUseId],
["Edge 12: Kitchen sink", KitchenSink],
];
for (const [name, Component] of tests) {
const html = ReactDOM.renderToString(React.createElement(Component));
console.log(`${name}:`);
console.log(` ${html}`);
console.log();
}
// Edge 7 needs two renders
console.log("Edge 7a: Conditional children (show=true):");
console.log(
` ${ReactDOM.renderToString(React.createElement(ConditionalChildren, { show: true }))}`,
);
console.log();
console.log("Edge 7b: Conditional children (show=false):");
console.log(
` ${ReactDOM.renderToString(React.createElement(ConditionalChildren, { show: false }))}`,
);
================================================
FILE: arch/server/test-useid.js
================================================
const React = require("react");
const ReactDOM = require("react-dom/server");
// Helper: component that renders a div with its useId value
function DivWithId() {
const id = React.useId();
return React.createElement("div", { id: id });
}
// Helper: component that calls useId twice
function DivWithTwoIds() {
const id1 = React.useId();
const id2 = React.useId();
return React.createElement("div", { "data-id1": id1, "data-id2": id2 });
}
// Helper: component that calls useId three times
function DivWithThreeIds() {
const id1 = React.useId();
const id2 = React.useId();
const id3 = React.useId();
return React.createElement("div", {
"data-id1": id1,
"data-id2": id2,
"data-id3": id3,
});
}
// Helper: wrapper component (no useId)
function Wrapper({ children }) {
return React.createElement("div", { className: "wrapper" }, children);
}
// Helper: component with useId that wraps children
function ParentWithId({ children }) {
const id = React.useId();
return React.createElement("div", { id: id }, children);
}
// Test 1: Single component with useId
function Test1() {
return React.createElement(DivWithId);
}
// Test 2: Two sibling components with useId
function Test2() {
return React.createElement(
"div",
null,
React.createElement(DivWithId),
React.createElement(DivWithId),
);
}
// Test 3: Nested components with useId
function Test3() {
return React.createElement(
ParentWithId,
null,
React.createElement(DivWithId),
);
}
// Test 4: Multiple useId calls in one component
function Test4() {
return React.createElement(DivWithTwoIds);
}
// Test 5: Three useId calls in one component
function Test5() {
return React.createElement(DivWithThreeIds);
}
// Test 6: Siblings with nested children
function Test6() {
return React.createElement(
"div",
null,
React.createElement(
ParentWithId,
null,
React.createElement(DivWithId),
),
React.createElement(DivWithId),
);
}
// Test 7: Deep nesting (3 levels)
function Test7() {
return React.createElement(
ParentWithId,
null,
React.createElement(
ParentWithId,
null,
React.createElement(DivWithId),
),
);
}
// Test 8: Wrapper (no useId) doesn't affect IDs
function Test8() {
return React.createElement(
Wrapper,
null,
React.createElement(DivWithId),
);
}
// Test 9: Three siblings
function Test9() {
return React.createElement(
"div",
null,
React.createElement(DivWithId),
React.createElement(DivWithId),
React.createElement(DivWithId),
);
}
// Test 10: Sibling components each with nested children
function Test10() {
return React.createElement(
"div",
null,
React.createElement(
ParentWithId,
null,
React.createElement(DivWithId),
React.createElement(DivWithId),
),
React.createElement(
ParentWithId,
null,
React.createElement(DivWithId),
),
);
}
// Test 11: Array/list of components (dynamic children via array)
function Test11() {
const items = [0, 1, 2].map((i) =>
React.createElement(DivWithId, { key: String(i) }),
);
return React.createElement("div", null, items);
}
// Test 12: Separate renderToString calls produce same IDs (reset per render)
function Test12a() {
return React.createElement(DivWithId);
}
function Test12b() {
return React.createElement(DivWithId);
}
const tests = [
["Test 1: Single component with useId", Test1],
["Test 2: Two sibling components", Test2],
["Test 3: Nested components", Test3],
["Test 4: Two useId calls in one component", Test4],
["Test 5: Three useId calls in one component", Test5],
["Test 6: Siblings with nested children", Test6],
["Test 7: Deep nesting (3 levels)", Test7],
["Test 8: Wrapper without useId", Test8],
["Test 9: Three siblings", Test9],
["Test 10: Complex siblings with nested", Test10],
["Test 11: Array/list of components", Test11],
];
for (const [name, Component] of tests) {
const html = ReactDOM.renderToString(React.createElement(Component));
console.log(`${name}:`);
console.log(` ${html}`);
console.log();
}
// Test 12: Separate renders
console.log("Test 12: Separate renders produce same IDs:");
const html12a = ReactDOM.renderToString(React.createElement(Test12a));
const html12b = ReactDOM.renderToString(React.createElement(Test12b));
console.log(` render1: ${html12a}`);
console.log(` render2: ${html12b}`);
console.log(` same: ${html12a === html12b}`);
================================================
FILE: benchmark/Makefile
================================================
# Server Reason React Benchmark Suite
# ==================================
.PHONY: all build build-native build-js clean help
.PHONY: bench bench-micro bench-memory bench-streaming bench-http
.PHONY: bench-render bench-render-native bench-render-bun bench-render-node
.PHONY: native-server js-servers
# Directories
BENCHMARK_DIR := $(shell pwd)
PROJECT_ROOT := $(shell dirname $(BENCHMARK_DIR))
RESULTS_DIR := $(BENCHMARK_DIR)/results
FRAMEWORKS_DIR := $(BENCHMARK_DIR)/frameworks
BUN := $(shell command -v bun 2>/dev/null || echo "$(HOME)/.bun/bin/bun")
# Optimization control
# Set DISABLE_OPTIMIZATIONS=1 to build without optimizations (--profile=dev)
# Default: optimizations enabled (--profile=release)
ifeq ($(DISABLE_OPTIMIZATIONS),1)
DUNE_PROFILE := --profile=dev
else
DUNE_PROFILE := --profile=release
endif
# Default target
all: help
# ============================================================================
# Build Targets
# ============================================================================
build: build-native build-js
@echo "✓ All builds complete"
build-native:
@echo "Building native OCaml benchmarks..."
@if [ "$(DISABLE_OPTIMIZATIONS)" = "1" ]; then \
echo " (Optimizations DISABLED)"; \
else \
echo " (Optimizations ENABLED)"; \
fi
cd $(PROJECT_ROOT) && opam exec -- dune build $(DUNE_PROFILE) benchmark/scenarios/benchmark_scenarios.a
cd $(PROJECT_ROOT) && opam exec -- dune build $(DUNE_PROFILE) benchmark/native/server.exe
cd $(PROJECT_ROOT) && opam exec -- dune build $(DUNE_PROFILE) benchmark/memory/memory_bench.exe
cd $(PROJECT_ROOT) && opam exec -- dune build $(DUNE_PROFILE) benchmark/streaming/streaming_bench.exe
@echo "✓ Native builds complete"
build-js:
@echo "Building JavaScript frameworks..."
cd $(FRAMEWORKS_DIR) && npm install
@echo "✓ JavaScript frameworks ready"
clean:
cd $(PROJECT_ROOT) && opam exec -- dune clean
rm -rf $(RESULTS_DIR)/*.json $(RESULTS_DIR)/*.md
rm -rf $(FRAMEWORKS_DIR)/node_modules
@echo "✓ Cleaned"
# ============================================================================
# Benchmark Targets
# ============================================================================
bench: bench-render bench-memory bench-streaming
@echo "✓ All benchmarks complete"
bench-memory: build-native
@echo "\n=== Running Memory Benchmarks ==="
$(PROJECT_ROOT)/_build/default/benchmark/memory/memory_bench.exe
bench-memory-json: build-native
@mkdir -p $(RESULTS_DIR)
$(PROJECT_ROOT)/_build/default/benchmark/memory/memory_bench.exe --json > $(RESULTS_DIR)/memory-$$(date +%Y%m%d-%H%M%S).json
bench-streaming: build-native
@echo "\n=== Running Streaming Benchmarks ==="
$(PROJECT_ROOT)/_build/default/benchmark/streaming/streaming_bench.exe
bench-render: build-native build-js
@echo "\n=== Pure Render Benchmark: Native vs Node vs Bun ==="
@echo "\n--- Native (server-reason-react) ---"
$(PROJECT_ROOT)/_build/default/benchmark/streaming/streaming_bench.exe
@echo "\n--- Node (React, NODE_ENV=production) ---"
@cd $(FRAMEWORKS_DIR) && ./node_modules/.bin/esbuild render-bench.ts --bundle --outfile=.render-bench-node.mjs --format=esm --platform=node --external:react --external:react-dom --loader:.jsx=jsx --loader:.ts=ts > /dev/null && NODE_ENV=production node .render-bench-node.mjs; rm -f $(FRAMEWORKS_DIR)/.render-bench-node.mjs
@echo "\n--- Bun (React, NODE_ENV=production) ---"
@cd $(FRAMEWORKS_DIR) && NODE_ENV=production $(BUN) render-bench.ts
bench-render-native: build-native
@echo "\n=== Native Render Benchmark ==="
$(PROJECT_ROOT)/_build/default/benchmark/streaming/streaming_bench.exe
bench-render-bun: build-js
@echo "\n=== Bun Render Benchmark (NODE_ENV=production) ==="
@cd $(FRAMEWORKS_DIR) && NODE_ENV=production $(BUN) render-bench.ts
bench-render-node: build-js
@echo "\n=== Node Render Benchmark (NODE_ENV=production) ==="
@cd $(FRAMEWORKS_DIR) && ./node_modules/.bin/esbuild render-bench.ts --bundle --outfile=.render-bench-node.mjs --format=esm --platform=node --external:react --external:react-dom --loader:.jsx=jsx --loader:.ts=ts > /dev/null && NODE_ENV=production node .render-bench-node.mjs; rm -f $(FRAMEWORKS_DIR)/.render-bench-node.mjs
bench-http: build
@echo "\n=== Running HTTP Benchmarks ==="
@mkdir -p $(RESULTS_DIR)
cd $(BENCHMARK_DIR)/runner && node runner.mjs
bench-http-quick: build
@echo "\n=== Running Quick HTTP Benchmarks ==="
cd $(BENCHMARK_DIR)/runner && node runner.mjs --scenarios trivial,table100
bench-native-http: build-native
@echo "\n=== Running Native HTTP Benchmarks ==="
cd $(BENCHMARK_DIR)/runner && node runner.mjs --frameworks dream-native
bench-js-http: build-js
@echo "\n=== Running JavaScript HTTP Benchmarks ==="
cd $(BENCHMARK_DIR)/runner && node runner.mjs --frameworks node-express,node-fastify,hono-node
# ============================================================================
# Server Targets (for manual testing)
# ============================================================================
native-server: build-native
@echo "Starting native Dream server on port 3000..."
PORT=3000 $(PROJECT_ROOT)/_build/default/benchmark/native/server.exe
js-servers: build-js
@echo "Starting all JavaScript servers..."
cd $(FRAMEWORKS_DIR) && npm run start:all
node-express: build-js
cd $(FRAMEWORKS_DIR) && npm run start:node-express
node-fastify: build-js
cd $(FRAMEWORKS_DIR) && npm run start:node-fastify
hono-node: build-js
cd $(FRAMEWORKS_DIR) && npm run start:hono-node
# ============================================================================
# Utility Targets
# ============================================================================
wrk-test:
@if [ -z "$(PORT)" ]; then \
echo "Usage: make wrk-test PORT=3000 [SCENARIO=table100]"; \
else \
wrk -t4 -c100 -d10s "http://localhost:$(PORT)/?scenario=$${SCENARIO:-table100}"; \
fi
scenarios: build-native
@$(PROJECT_ROOT)/_build/default/benchmark/native/server.exe &
@sleep 1
@curl -s http://localhost:3000/scenarios | jq .
@pkill -f "server.exe" || true
results:
@ls -la $(RESULTS_DIR)/*.md $(RESULTS_DIR)/*.json 2>/dev/null || echo "No results yet. Run 'make bench-http'"
# ============================================================================
# Help
# ============================================================================
help:
@echo "Server Reason React Benchmark Suite"
@echo "===================================="
@echo ""
@echo "Build targets:"
@echo " make build - Build all (native + JS)"
@echo " make build-native - Build native OCaml benchmarks"
@echo " make build-js - Install JS framework dependencies"
@echo " make clean - Clean all build artifacts"
@echo ""
@echo "Benchmark targets:"
@echo " make bench - Run all benchmarks"
@echo " make bench-render - Pure render comparison: Native vs Node vs Bun (recommended)"
@echo " make bench-render-native - Native render only"
@echo " make bench-render-node - Node render only (NODE_ENV=production)"
@echo " make bench-render-bun - Bun render only (NODE_ENV=production)"
@echo " make bench-memory - Run memory benchmarks"
@echo " make bench-streaming - Run streaming benchmarks"
@echo " make bench-http - Run HTTP load testing (all frameworks)"
@echo " make bench-http-quick - Quick HTTP test (trivial + table100 only)"
@echo " make bench-native-http - HTTP test native only"
@echo " make bench-js-http - HTTP test JS frameworks only"
@echo ""
@echo "Server targets (for manual testing):"
@echo " make native-server - Start native Dream server (port 3000)"
@echo " make js-servers - Start all JS servers"
@echo " make node-express - Start Node.js + Express"
@echo " make node-fastify - Start Node.js + Fastify"
@echo " make hono-node - Start Hono + Node.js"
@echo ""
@echo "Utility:"
@echo " make wrk-test PORT=3000 [SCENARIO=table100]"
@echo " - Quick load test against running server"
@echo " make scenarios - List available scenarios"
@echo " make results - Show benchmark results"
@echo ""
@echo "Optimization control:"
@echo " DISABLE_OPTIMIZATIONS=1 make build-native"
@echo " - Build without optimizations (--profile=dev)"
@echo " make build-native - Build with optimizations (--profile=release, default)"
================================================
FILE: benchmark/README.md
================================================
# Server Reason React Benchmark Suite
A comprehensive benchmark suite for measuring and comparing SSR performance of `server-reason-react` against React.js on JavaScript runtimes.
## Performance Summary
Pure rendering performance — `server-reason-react` (release profile, flambda + PPX static analyzer enabled) vs React 19.2.5 on Node 22.22.0 and Bun 1.3.12, both with `NODE_ENV=production`. Numbers are mean of 100 iterations, `renderToString`.
All 17 scenarios the native bench exercises are mirrored in JS, so every row is a like-for-like comparison of the same component tree rendered by three different runtimes.
| Scenario | SRR | Node + React | Bun + React | SRR vs Node | SRR vs Bun |
|-------------|------------:|-------------:|------------:|------------:|-----------:|
| Trivial | **0.29 µs** | 42.10 µs | 44.54 µs | **145x** | **154x** |
| ShallowTree | **16.78 µs**| 99.97 µs | 101.17 µs | **6.0x** | **6.0x** |
| DeepTree10 | **13.74 µs**| 35.93 µs | 35.97 µs | **2.6x** | **2.6x** |
| DeepTree50 | **67.07 µs**| 91.77 µs | 105.36 µs | **1.4x** | **1.6x** |
| WideTree10 | **14.78 µs**| 66.20 µs | 96.20 µs | **4.5x** | **6.5x** |
| WideTree100 | **216.24 µs**| 382.75 µs | 346.96 µs | **1.8x** | **1.6x** |
| WideTree500 | **1.15 ms** | 2.30 ms | 1.55 ms | **2.0x** | **1.3x** |
| Table10 | **35.95 µs**| 219.12 µs | 200.06 µs | **6.1x** | **5.6x** |
| Table100 | **301.89 µs**| 714.19 µs | 628.45 µs | **2.4x** | **2.1x** |
| Table500 | **1.35 ms** | 4.97 ms | 3.05 ms | **3.7x** | **2.3x** |
| PropsSmall | **107.42 µs**| 217.50 µs | 204.00 µs | **2.0x** | **1.9x** |
| PropsMedium | **305.64 µs**| 395.08 µs | 361.21 µs | **1.3x** | **1.2x** |
| Ecommerce24 | **121.24 µs**| 373.23 µs | 376.76 µs | **3.1x** | **3.1x** |
| Ecommerce48 | **228.68 µs**| 488.66 µs | 492.02 µs | **2.1x** | **2.2x** |
| Dashboard | **31.68 µs**| 85.82 µs | 94.09 µs | **2.7x** | **3.0x** |
| Blog50 | **182.86 µs**| 565.45 µs | 587.69 µs | **3.1x** | **3.2x** |
| Form | **69.39 µs**| 206.54 µs | 163.92 µs | **3.0x** | **2.4x** |
Throughput (MB/s, higher is better):
| Scenario | SRR | Node | Bun |
|-------------|----:|-----:|----:|
| Table500 | **505.2** | 137.2 | 223.6 |
| Blog50 | **500.8** | 161.9 | 155.8 |
| Table100 | **455.4** | 192.5 | 218.8 |
| Dashboard | **440.2** | 162.5 | 148.2 |
| WideTree10 | **439.2** | 98.1 | 67.5 |
| Ecommerce24 | **427.3** | 138.8 | 137.5 |
| Table10 | **421.1** | 69.1 | 75.7 |
| Ecommerce48 | **410.6** | 192.1 | 190.8 |
| Form | **310.6** | 104.4 | 131.5 |
| PropsMedium | **302.6** | 234.1 | 256.0 |
| WideTree500 | **282.3** | 141.1 | 209.4 |
Notes:
- **SRR wins on every scenario.** Typical margin is 1.3–3.1x on realistic pages; the largest wins are on Table10 (5.6–6.1x), Table500 (2.3–3.7x), ShallowTree (6.0x), and WideTree10 (4.5–6.5x). Trivial's ~145x is measurement floor noise, not a real rendering difference.
- **Bun beats Node** on large wide trees and tables (1.3–1.6x), but loses on attribute-heavy and small-tree scenarios. On most real-page scenarios the two are within 10% of each other.
- React 19 `renderToString` is measurably slower than 18.3.1 on small scenarios (Trivial went from ~19 µs → ~42 µs) because of added server-component machinery that runs on every render. Large scenarios are roughly on par or slightly faster.
> These benchmarks measure pure `renderToStaticMarkup`/`renderToString` performance without HTTP server overhead. JS runs require `NODE_ENV=production`; the harness refuses to run otherwise (React's dev build is 3–7x slower and would give meaningless numbers).
## Quick Start
```bash
# Build everything
make build
# Run all benchmarks
make bench
# Run the render comparision
make bench-render
```
## Optimization Control
By default, benchmarks are built with optimizations enabled (`--profile=release`). You can disable optimizations to compare performance:
```bash
# Build without optimizations
DISABLE_OPTIMIZATIONS=1 make build-native
# Run benchmarks without optimizations
DISABLE_OPTIMIZATIONS=1 make bench-render
# Re-enable optimizations (default)
make build-native
make bench-render
```
This is useful for:
- Comparing optimized vs unoptimized performance
- CI workflows that need to test both configurations
- Debugging performance differences
## Benchmark Categories
### 1. Render Comparison (`bench-render`)
**The official benchmark** — compares pure rendering performance without HTTP overhead:
```bash
make bench-render # Native vs Bun side-by-side
make bench-render-native # Native only
make bench-render-bun # Bun only
```
This measures `renderToStaticMarkup` (native) vs `renderToString` (Bun/React) directly, giving the most accurate comparison of SSR performance.
### 2. Memory Benchmarks (`bench-memory`)
Measures allocation and GC behavior:
- Words allocated per render
- Minor/major GC cycles
- Memory efficiency (output bytes per word)
```bash
make bench-memory
make bench-memory-json # Output JSON for CI
```
### 3. Streaming Benchmarks (`bench-streaming`)
Compares `renderToStaticMarkup` vs `renderToString` on native:
- Time to render
- Throughput (MB/s)
- Overhead comparison
```bash
make bench-streaming
```
### 4. HTTP Load Testing (`bench-http`)
Full end-to-end HTTP benchmarks using wrk:
- Requests/second
- Latency distribution (avg, p99)
- Transfer rate
- Multi-framework comparison
```bash
make bench-http # Full suite, all frameworks
make bench-http-quick # Quick test (trivial + table100)
make bench-native-http # Native only
make bench-js-http # JavaScript frameworks only
```
## Test Scenarios
| Scenario | Description | Purpose |
|----------|-------------|---------|
| `trivial` | Hello world | Baseline overhead |
| `shallow` | 5-level component tree | Prop passing |
| `deep10-100` | 10-100 level deep tree | Recursion performance |
| `wide10-1000` | 10-1000 siblings | Array rendering |
| `table10-500` | Data table rows | Real-world tables |
| `props_*` | Many HTML attributes | Attribute serialization |
| `ecommerce24-100` | Product grid | E-commerce SSR |
| `dashboard` | Analytics page | Admin UI |
| `blog10-100` | Article + comments | Content-heavy |
| `form` | Multi-step form | Form rendering |
## Framework Comparison
| Framework | Port | Description |
|-----------|------|-------------|
| `dream-native` | 3000 | OCaml + Dream + server-reason-react |
| `node-express` | 3001 | Node.js + Express + React |
| `node-fastify` | 3002 | Node.js + Fastify + React |
| `hono-node` | 3003 | Hono + Node.js + React |
| `hono-bun` | 3004 | Hono + Bun + React |
| `bun-native` | 3005 | Bun native + React |
| `preact` | 3006 | Node.js + Express + Preact |
## Running Individual Servers
For manual testing or debugging:
```bash
# Start servers individually
make native-server # Dream on :3000
make node-express # Express on :3001
make node-fastify # Fastify on :3002
make hono-node # Hono on :3003
DISABLE_LOGGER=1 make native-server
# Test with curl
curl "http://localhost:3000/?scenario=table100"
curl "http://localhost:3000/scenarios" # List available
# Quick wrk test
make wrk-test PORT=3000 SCENARIO=table100
```
## Methodology
### Warmup
- 100 requests before measurement to eliminate cold-start effects
- GC stabilization between benchmark runs
### Measurement
- Multiple iterations with statistical analysis
- Core_bench: 10,000 calls per benchmark
- HTTP: 10-30 seconds with wrk (configurable)
### Environment
- All frameworks run single-threaded for fair comparison
- React runtimes require `NODE_ENV=production` — `render-bench.ts` refuses to run otherwise, and `make bench-render` sets it automatically. React's development build skips a large amount of dev-only validation in production and is 3–7x faster as a result.
- Same React/component tree across frameworks
### Metrics Reported
- **Throughput**: Requests/second
- **Latency**: Average, p50, p99, max
- **Memory**: Words allocated, GC cycles
- **Transfer**: Bytes/second
## Requirements
- OCaml 5.x with opam
- Node.js 20+ (for JS frameworks)
- Bun (optional, for Bun benchmarks)
- wrk (optional, for HTTP load testing)
## Results
Results are saved to `benchmark/results/`:
- `benchmark-TIMESTAMP.json` - Raw data
- `benchmark-TIMESTAMP.md` - Markdown report
View latest results:
```bash
make results
```
## Interpreting Results
### What to look for:
1. **Render time (µs)**: Lower is better. Time to render a component tree.
2. **Throughput (MB/s)**: Higher is better. Data output rate.
3. **Memory (words/iter)**: Lower is better. Memory efficiency.
4. **Requests/sec** (HTTP): Higher is better. End-to-end throughput.
### Typical results pattern:
**Pure rendering** (no HTTP, React runtimes in `NODE_ENV=production`):
- SRR is **1.3–6.1x faster** than Node + React and **1.2–6.5x faster** than Bun + React across all 17 scenarios. The biggest realistic wins are on tables (up to 6.1x on Table10 vs Node) and shallow/wide component trees (4.5–6.5x on WideTree10).
- SRR throughput tops out around **505 MB/s** on tables and **500 MB/s** on content pages (Blog50). Bun is competitive on a handful of scenarios (~225 MB/s on Table500); Node trails at ~100–195 MB/s on large scenarios.
- The "trivial" scenario shows a ~145x gap that's mostly measurement floor noise, not a real rendering difference.
- Node and Bun are roughly at parity on most real-page scenarios. Bun wins on large wide trees and tables; Node wins on attribute-heavy and small-tree scenarios.
**Inline `style` props are expensive in SRR.** `ReactDOM.Style.make` has ~347 optional args and allocates ~1,460 words per call regardless of what's passed. If a component is on a hot render path and uses inline `style`, consider moving the style into a CSS class. Scenarios in this suite use `className` so the measurement reflects the rendering path, not `Style.make` allocation.
**HTTP benchmarks** (with server overhead):
- Bun's HTTP layer is faster for minimal requests (trivial scenario)
- Native wins on real SSR workloads where rendering dominates
- Use `DISABLE_LOGGER=1` for accurate native HTTP benchmarks
## Contributing
To add a new scenario:
1. Create `benchmark/scenarios/NewScenario.re`
2. Add to the scenario list in:
- `benchmark/native/server.re`
- `benchmark/micro/micro_bench.re`
- `benchmark/frameworks/shared/scenarios.jsx`
3. Run benchmarks: `make bench`
================================================
FILE: benchmark/allocation.ml
================================================
let measure_alloc title f =
let before = Gc.stat () in
let result = f () in
let after = Gc.stat () in
Printf.printf "\n=== %s ===\n" title;
Printf.printf "Minor words: %f\n" (after.minor_words -. before.minor_words);
Printf.printf "Major words: %f\n" (after.major_words -. before.major_words);
Printf.printf "Minor collections: %d\n" (after.minor_collections - before.minor_collections);
result
let loop n f =
for _ = 1 to n do
f ()
done
let main () =
let filter_map_style () =
let element =
React.createElement "div"
(Stdlib.List.filter_map Fun.id
(List.init 50 (fun i ->
Some
(React.JSX.String (Printf.sprintf "prop%d" i, Printf.sprintf "prop%d" i, Printf.sprintf "value%d" i)))))
[]
in
let _ = ReactDOM.renderToStaticMarkup element in
()
in
let direct_style () =
let props =
List.init 50 (fun i ->
React.JSX.String (Printf.sprintf "prop%d" i, Printf.sprintf "prop%d" i, Printf.sprintf "value%d" i))
in
let element = React.createElement "div" props [] in
let _ = ReactDOM.renderToStaticMarkup element in
()
in
let render_hello_world () =
let _ = ReactDOM.renderToStaticMarkup (Static_small.make (Static_small.makeProps ())) in
()
in
let render_app () =
let _ = ReactDOM.renderToStaticMarkup (App.make (App.makeProps ())) in
()
in
let render_React_list ~num () =
let list = React.list (List.init num (fun i -> React.string (Printf.sprintf "index: %d" i))) in
let _ = ReactDOM.renderToStaticMarkup (React.createElement "div" [] [ list ]) in
()
in
let render_React_array ~num () =
let array = React.array (Array.init num (fun i -> React.string (Printf.sprintf "index: %d" i))) in
let _ = ReactDOM.renderToStaticMarkup (React.createElement "div" [] [ array ]) in
()
in
measure_alloc "Use filter_map" (fun () -> loop 10000 filter_map_style);
measure_alloc "Use list direct style" (fun () -> loop 10000 direct_style);
measure_alloc "Render <HelloWorld />" (fun () -> loop 10000 render_hello_world);
measure_alloc "Render <App />" (fun () -> loop 10000 render_app);
measure_alloc "Render React.list 10" (fun () -> loop 10000 (render_React_list ~num:10));
measure_alloc "Render React.array 10" (fun () -> loop 10000 (render_React_array ~num:10));
measure_alloc "Render React.list 100" (fun () -> loop 10000 (render_React_list ~num:100));
measure_alloc "Render React.array 100" (fun () -> loop 10000 (render_React_array ~num:100));
(* measure_alloc "Render React.list 1000" (fun () -> loop 10000 (render_React_list ~num:1000)); *)
(* measure_alloc "Render React.array 1000" (fun () -> loop 10000 (render_React_array ~num:1000)); *)
Lwt.return ()
let () = Lwt_main.run (main ())
================================================
FILE: benchmark/bench.ml
================================================
(* Benchmark runner with JSON output for github-action-benchmark (customBiggerIsBetter) *)
let json_mode = ref false
let iterations = 10000
type benchmark_result = { name : string; ops_per_sec : float }
let measure_benchmark ~name render_fn =
for _ = 1 to 100 do
let _ = render_fn () in
()
done;
Gc.full_major ();
Gc.compact ();
let start = Unix.gettimeofday () in
for _ = 1 to iterations do
let _ = render_fn () in
()
done;
let elapsed = Unix.gettimeofday () -. start in
let ops_per_sec = float_of_int iterations /. elapsed in
{ name; ops_per_sec }
let lwt_iterations = 1000
let measure_benchmark_lwt ~name render_fn =
for _ = 1 to 10 do
let _ = Lwt_main.run (render_fn ()) in
()
done;
Gc.full_major ();
Gc.compact ();
let start = Unix.gettimeofday () in
for _ = 1 to lwt_iterations do
let _ = Lwt_main.run (render_fn ()) in
()
done;
let elapsed = Unix.gettimeofday () -. start in
let ops_per_sec = float_of_int lwt_iterations /. elapsed in
{ name; ops_per_sec }
let print_result r = Printf.printf "%-35s %12.0f ops/sec\n" r.name r.ops_per_sec
let print_results_table results =
Printf.printf "\n%s\n" (String.make 50 '=');
Printf.printf "server-reason-react Benchmarks\n";
Printf.printf "%s\n" (String.make 50 '=');
Printf.printf "%-35s %12s\n" "Benchmark" "Throughput";
Printf.printf "%s\n" (String.make 50 '-');
List.iter print_result results;
Printf.printf "%s\n" (String.make 50 '=')
let print_results_json results =
let json_entries =
List.map
(fun r -> Printf.sprintf {| {"name": "%s", "unit": "ops/sec", "value": %.2f}|} r.name r.ops_per_sec)
results
in
print_endline "[";
print_endline (String.concat ",\n" json_entries);
print_endline "]"
let () =
let args = Array.to_list Sys.argv in
json_mode := List.mem "--json" args;
let open Benchmark_scenarios in
let results =
[
measure_benchmark ~name:"trivial/renderToStaticMarkup" (fun () ->
ReactDOM.renderToStaticMarkup (Trivial.make (Trivial.makeProps ())));
measure_benchmark ~name:"trivial/renderToString" (fun () ->
ReactDOM.renderToString (Trivial.make (Trivial.makeProps ())));
measure_benchmark ~name:"depth/10" (fun () ->
ReactDOM.renderToStaticMarkup (DeepTree.Depth10.make (DeepTree.Depth10.makeProps ())));
measure_benchmark ~name:"depth/25" (fun () ->
ReactDOM.renderToStaticMarkup (DeepTree.Depth25.make (DeepTree.Depth25.makeProps ())));
measure_benchmark ~name:"depth/50" (fun () ->
ReactDOM.renderToStaticMarkup (DeepTree.Depth50.make (DeepTree.Depth50.makeProps ())));
measure_benchmark ~name:"depth/100" (fun () ->
ReactDOM.renderToStaticMarkup (DeepTree.Depth100.make (DeepTree.Depth100.makeProps ())));
measure_benchmark ~name:"width/10" (fun () ->
ReactDOM.renderToStaticMarkup (WideTree.Wide10.make (WideTree.Wide10.makeProps ())));
measure_benchmark ~name:"width/100" (fun () ->
ReactDOM.renderToStaticMarkup (WideTree.Wide100.make (WideTree.Wide100.makeProps ())));
measure_benchmark ~name:"width/500" (fun () ->
ReactDOM.renderToStaticMarkup (WideTree.Wide500.make (WideTree.Wide500.makeProps ())));
measure_benchmark ~name:"width/1000" (fun () ->
ReactDOM.renderToStaticMarkup (WideTree.Wide1000.make (WideTree.Wide1000.makeProps ())));
measure_benchmark ~name:"table/10" (fun () ->
ReactDOM.renderToStaticMarkup (Table.Table10.make (Table.Table10.makeProps ())));
measure_benchmark ~name:"table/50" (fun () ->
ReactDOM.renderToStaticMarkup (Table.Table50.make (Table.Table50.makeProps ())));
measure_benchmark ~name:"table/100" (fun () ->
ReactDOM.renderToStaticMarkup (Table.Table100.make (Table.Table100.makeProps ())));
measure_benchmark ~name:"table/500" (fun () ->
ReactDOM.renderToStaticMarkup (Table.Table500.make (Table.Table500.makeProps ())));
measure_benchmark ~name:"props/small" (fun () ->
ReactDOM.renderToStaticMarkup (PropsHeavy.Small.make (PropsHeavy.Small.makeProps ())));
measure_benchmark ~name:"props/medium" (fun () ->
ReactDOM.renderToStaticMarkup (PropsHeavy.Medium.make (PropsHeavy.Medium.makeProps ())));
measure_benchmark ~name:"props/large" (fun () ->
ReactDOM.renderToStaticMarkup (PropsHeavy.Large.make (PropsHeavy.Large.makeProps ())));
measure_benchmark ~name:"realworld/ecommerce24" (fun () ->
ReactDOM.renderToStaticMarkup (Ecommerce.Products24.make (Ecommerce.Products24.makeProps ())));
measure_benchmark ~name:"realworld/ecommerce48" (fun () ->
ReactDOM.renderToStaticMarkup (Ecommerce.Products48.make (Ecommerce.Products48.makeProps ())));
measure_benchmark ~name:"realworld/dashboard" (fun () ->
ReactDOM.renderToStaticMarkup (Dashboard.make (Dashboard.makeProps ())));
measure_benchmark ~name:"realworld/blog50" (fun () ->
ReactDOM.renderToStaticMarkup (Blog.Blog50.make (Blog.Blog50.makeProps ())));
measure_benchmark ~name:"realworld/form" (fun () -> ReactDOM.renderToStaticMarkup (Form.make (Form.makeProps ())));
measure_benchmark ~name:"primitive/React.string" (fun () -> ReactDOM.renderToStaticMarkup (React.string "Hello"));
measure_benchmark ~name:"primitive/React.int" (fun () -> ReactDOM.renderToStaticMarkup (React.int 42));
measure_benchmark ~name:"primitive/React.null" (fun () -> ReactDOM.renderToStaticMarkup React.null);
measure_benchmark ~name:"primitive/createElement_empty" (fun () ->
ReactDOM.renderToStaticMarkup (React.createElement "div" [] []));
measure_benchmark ~name:"primitive/createElement_children" (fun () ->
let children = List.init 10 (fun i -> React.string (string_of_int i)) in
ReactDOM.renderToStaticMarkup (React.createElement "div" [] children));
measure_benchmark ~name:"primitive/React.array_10" (fun () ->
let arr = Array.init 10 (fun i -> React.string (string_of_int i)) in
ReactDOM.renderToStaticMarkup (React.createElement "div" [] [ React.array arr ]));
measure_benchmark ~name:"primitive/React.array_100" (fun () ->
let arr = Array.init 100 (fun i -> React.string (string_of_int i)) in
ReactDOM.renderToStaticMarkup (React.createElement "div" [] [ React.array arr ]));
measure_benchmark ~name:"primitive/React.list_10" (fun () ->
let lst = List.init 10 (fun i -> React.string (string_of_int i)) in
ReactDOM.renderToStaticMarkup (React.createElement "div" [] [ React.list lst ]));
measure_benchmark ~name:"primitive/React.list_100" (fun () ->
let lst = List.init 100 (fun i -> React.string (string_of_int i)) in
ReactDOM.renderToStaticMarkup (React.createElement "div" [] [ React.list lst ]));
measure_benchmark_lwt ~name:"rsc/trivial" (fun () ->
let%lwt html, _subscribe = ReactServerDOM.render_html (Trivial.make (Trivial.makeProps ())) in
Lwt.return html);
measure_benchmark_lwt ~name:"rsc/depth/50" (fun () ->
let%lwt html, _subscribe =
ReactServerDOM.render_html (DeepTree.Depth50.make (DeepTree.Depth50.makeProps ()))
in
Lwt.return html);
measure_benchmark_lwt ~name:"rsc/width/100" (fun () ->
let%lwt html, _subscribe =
ReactServerDOM.render_html (WideTree.Wide100.make (WideTree.Wide100.makeProps ()))
in
Lwt.return html);
measure_benchmark_lwt ~name:"rsc/width/500" (fun () ->
let%lwt html, _subscribe =
ReactServerDOM.render_html (WideTree.Wide500.make (WideTree.Wide500.makeProps ()))
in
Lwt.return html);
measure_benchmark_lwt ~name:"rsc/width/1000" (fun () ->
let%lwt html, _subscribe =
ReactServerDOM.render_html (WideTree.Wide1000.make (WideTree.Wide1000.makeProps ()))
in
Lwt.return html);
measure_benchmark_lwt ~name:"rsc/table/100" (fun () ->
let%lwt html, _subscribe = ReactServerDOM.render_html (Table.Table100.make (Table.Table100.makeProps ())) in
Lwt.return html);
measure_benchmark_lwt ~name:"rsc/table/500" (fun () ->
let%lwt html, _subscribe = ReactServerDOM.render_html (Table.Table500.make (Table.Table500.makeProps ())) in
Lwt.return html);
]
in
if !json_mode then print_results_json results else print_results_table results
================================================
FILE: benchmark/dune
================================================
(executable
(name bench)
(modules bench)
(libraries
unix
lwt
lwt.unix
benchmark_scenarios
server-reason-react.js
server-reason-react.react
server-reason-react.reactDom)
(preprocess
(pps server-reason-react.ppx lwt_ppx)))
(executable
(name allocation)
(modules allocation)
(libraries
unix
lwt
lwt.unix
server-reason-react.js
server-reason-react.react
server-reason-react.reactDom
demo_shared_native)
(preprocess
(pps server-reason-react.ppx)))
(rule
(alias bench)
(action
(run ./bench.exe)))
(rule
(alias bench-json)
(action
(run ./bench.exe --json)))
================================================
FILE: benchmark/frameworks/bun-native/server.tsx
================================================
/**
* Bun Native + React SSR Benchmark Server
*/
import React from "react";
import ReactDOMServer from "react-dom/server";
import * as scenarios from "../shared/scenarios.jsx";
const PORT = Number(process.env.PORT) || 3005;
Bun.serve({
port: PORT,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/health") {
return Response.json({
status: "ok",
framework: "bun-native",
pid: process.pid,
});
}
if (url.pathname === "/scenarios") {
const list = Object.entries(scenarios.scenarios).map(([key, val]) => ({
key,
name: val.name,
description: val.description,
}));
return Response.json(list);
}
if (url.pathname === "/") {
const scenarioName = url.searchParams.get("scenario") || "table100";
const scenario = scenarios.scenarios[scenarioName as keyof typeof scenarios.scenarios];
if (!scenario) {
return new Response(`Unknown scenario: ${scenarioName}`, { status: 404 });
}
const Component = scenario.component;
const html = ReactDOMServer.renderToString(React.createElement(Component));
return new Response(
`<!DOCTYPE html><html><head><title>Benchmark</title></head><body><div id="root">${html}</div></body></html>`,
{
headers: { "Content-Type": "text/html" },
}
);
}
return new Response("Not Found", { status: 404 });
},
});
console.log(`[bun-native] Server listening on http://localhost:${PORT}`);
console.log(`[bun-native] PID: ${process.pid}`);
================================================
FILE: benchmark/frameworks/hono-bun/server.ts
================================================
/**
* Hono + Bun + React SSR Benchmark Server
*/
import { Hono } from "hono";
import React from "react";
import ReactDOMServer from "react-dom/server";
import * as scenarios from "../shared/scenarios.jsx";
const app = new Hono();
const PORT = Number(process.env.PORT) || 3004;
// Scenario selection via query param
app.get("/", (c) => {
const scenarioName = c.req.query("scenario") || "table100";
const scenario = scenarios.scenarios[scenarioName as keyof typeof scenarios.scenarios];
if (!scenario) {
c.status(404);
return c.text(`Unknown scenario: ${scenarioName}`);
}
const Component = scenario.component;
const html = ReactDOMServer.renderToString(React.createElement(Component));
return c.html(
`<!DOCTYPE html><html><head><title>Benchmark</title></head><body><div id="root">${html}</div></body></html>`
);
});
// Health check
app.get("/health", (c) => {
return c.json({ status: "ok", framework: "hono-bun", pid: process.pid });
});
// List available scenarios
app.get("/scenarios", (c) => {
const list = Object.entries(scenarios.scenarios).map(([key, val]) => ({
key,
name: val.name,
description: val.description,
}));
return c.json(list);
});
export default {
port: PORT,
fetch: app.fetch,
};
console.log(`[hono-bun] Server listening on http://localhost:${PORT}`);
console.log(`[hono-bun] PID: ${process.pid}`);
================================================
FILE: benchmark/frameworks/hono-node/server.mjs
================================================
/**
* Hono + Node.js + React SSR Benchmark Server
*/
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import React from "react";
import ReactDOMServer from "react-dom/server";
import * as scenarios from "../shared/scenarios.jsx";
const app = new Hono();
const PORT = process.env.PORT || 3003;
// Scenario selection via query param
app.get("/", (c) => {
const scenarioName = c.req.query("scenario") || "table100";
const scenario = scenarios.scenarios[scenarioName];
if (!scenario) {
c.status(404);
return c.text(`Unknown scenario: ${scenarioName}`);
}
const Component = scenario.component;
const html = ReactDOMServer.renderToString(React.createElement(Component));
return c.html(
`<!DOCTYPE html><html><head><title>Benchmark</title></head><body><div id="root">${html}</div></body></html>`
);
});
// Health check
app.get("/health", (c) => {
return c.json({ status: "ok", framework: "hono-node", pid: process.pid });
});
// List available scenarios
app.get("/scenarios", (c) => {
const list = Object.entries(scenarios.scenarios).map(([key, val]) => ({
key,
name: val.name,
description: val.description,
}));
return c.json(list);
});
serve(
{
fetch: app.fetch,
port: PORT,
},
(info) => {
console.log(`[hono-node] Server listening on http://localhost:${info.port}`);
console.log(`[hono-node] PID: ${process.pid}`);
}
);
================================================
FILE: benchmark/frameworks/node-express/server.mjs
================================================
/**
* Node.js + Express + React SSR Benchmark Server
*/
import express from "express";
import React from "react";
import ReactDOMServer from "react-dom/server";
import * as scenarios from "../shared/scenarios.jsx";
const app = express();
const PORT = process.env.PORT || 3001;
// Scenario selection via query param
app.get("/", (req, res) => {
const scenarioName = req.query.scenario || "table100";
const scenario = scenarios.scenarios[scenarioName];
if (!scenario) {
return res.status(404).send(`Unknown scenario: ${scenarioName}`);
}
const Component = scenario.component;
const html = ReactDOMServer.renderToString(React.createElement(Component));
res.setHeader("Content-Type", "text/html");
res.send(`<!DOCTYPE html><html><head><title>Benchmark</title></head><body><div id="root">${html}</div></body></html>`);
});
// Health check
app.get("/health", (req, res) => {
res.json({ status: "ok", framework: "node-express", pid: process.pid });
});
// List available scenarios
app.get("/scenarios", (req, res) => {
const list = Object.entries(scenarios.scenarios).map(([key, val]) => ({
key,
name: val.name,
description: val.description,
}));
res.json(list);
});
app.listen(PORT, () => {
console.log(`[node-express] Server listening on http://localhost:${PORT}`);
console.log(`[node-express] PID: ${process.pid}`);
});
================================================
FILE: benchmark/frameworks/node-fastify/server.mjs
================================================
/**
* Node.js + Fastify + React SSR Benchmark Server
*/
import Fastify from "fastify";
import React from "react";
import ReactDOMServer from "react-dom/server";
import * as scenarios from "../shared/scenarios.jsx";
const fastify = Fastify({
logger: false,
});
const PORT = process.env.PORT || 3002;
// Scenario selection via query param
fastify.get("/", async (request, reply) => {
const scenarioName = request.query.scenario || "table100";
const scenario = scenarios.scenarios[scenarioName];
if (!scenario) {
reply.code(404);
return `Unknown scenario: ${scenarioName}`;
}
const Component = scenario.component;
const html = ReactDOMServer.renderToString(React.createElement(Component));
reply.type("text/html");
return `<!DOCTYPE html><html><head><title>Benchmark</title></head><body><div id="root">${html}</div></body></html>`;
});
// Health check
fastify.get("/health", async () => {
return { status: "ok", framework: "node-fastify", pid: process.pid };
});
// List available scenarios
fastify.get("/scenarios", async () => {
return Object.entries(scenarios.scenarios).map(([key, val]) => ({
key,
name: val.name,
description: val.description,
}));
});
const start = async () => {
try {
await fastify.listen({ port: PORT, host: "0.0.0.0" });
console.log(`[node-fastify] Server listening on http://localhost:${PORT}`);
console.log(`[node-fastify] PID: ${process.pid}`);
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};
start();
================================================
FILE: benchmark/frameworks/package.json
================================================
{
"name": "benchmark-frameworks",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"build:shared": "esbuild shared/App.jsx --bundle --outfile=dist/App.js --format=cjs --platform=node --external:react --external:react-dom",
"start:node-express": "NODE_ENV=production node node-express/server.mjs",
"start:node-fastify": "NODE_ENV=production node node-fastify/server.mjs",
"start:hono-node": "NODE_ENV=production node hono-node/server.mjs",
"start:hono-bun": "NODE_ENV=production bun hono-bun/server.ts",
"start:bun": "NODE_ENV=production bun bun-native/server.tsx",
"start:preact": "NODE_ENV=production node preact/server.mjs",
"start:all": "concurrently -n 'express,fastify,hono-node,preact' 'npm run start:node-express' 'npm run start:node-fastify' 'npm run start:hono-node' 'npm run start:preact'"
},
"dependencies": {
"@hono/node-server": "^1.13.1",
"express": "^4.21.0",
"fastify": "^5.0.0",
"hono": "^4.6.3",
"preact": "^10.24.1",
"preact-render-to-string": "^6.5.10",
"react": "^19.1.0",
"react-dom": "^19.1.0"
},
"devDependencies": {
"@types/bun": "^1.1.10",
"@types/node": "^22.7.4",
"concurrently": "^9.0.1",
"esbuild": "^0.24.0",
"typescript": "^5.6.2"
}
}
================================================
FILE: benchmark/frameworks/preact/server.mjs
================================================
/**
* Node.js + Express + Preact SSR Benchmark Server
* Tests Preact's lighter-weight alternative to React
*/
import express from "express";
import { h } from "preact";
import renderToString from "preact-render-to-string";
const app = express();
const PORT = process.env.PORT || 3006;
// ============================================================================
// Preact-native scenarios (mirrors React scenarios)
// ============================================================================
const Trivial = () => h("div", null, "Hello World");
// ShallowTree
const Level5 = ({ title, subtitle, active, count }) =>
h(
"div",
{ class: `p-4 rounded ${active ? "bg-blue-500" : ""}` },
h("h5", { class: "font-bold" }, title),
h("p", { class: "text-sm text-gray-500" }, subtitle),
h("span", { class: "badge" }, count)
);
const Level4 = ({ title, description, isHighlighted, itemCount }) =>
h(
"section",
{ class: `mb-4 ${isHighlighted ? "border-l-4 border-blue-500" : ""}` },
h(Level5, { title, subtitle: description, active: isHighlighted, count: itemCount }),
h(Level5, { title: `${title} Alt`, subtitle: "Secondary", active: false, count: itemCount * 2 })
);
const Level3 = ({ groupName, expanded, totalItems }) =>
h(
"article",
{ class: `p-6 ${expanded ? "shadow-lg" : ""}` },
h("h3", { class: "text-xl font-semibold mb-4" }, groupName),
h(Level4, { title: "First Item", description: "Description A", isHighlighted: true, itemCount: totalItems }),
h(Level4, { title: "Second Item", description: "Description B", isHighlighted: false, itemCount: Math.floor(totalItems / 2) })
);
const Level2 = ({ sectionTitle, isVisible }) =>
h(
"div",
{ class: `container mx-auto ${isVisible ? "block" : ""}` },
h("h2", { class: "text-2xl font-bold mb-6" }, sectionTitle),
h(Level3, { groupName: "Group Alpha", expanded: true, totalItems: 42 }),
h(Level3, { groupName: "Group Beta", expanded: false, totalItems: 17 })
);
const Level1 = ({ pageTitle }) =>
h(
"main",
{ class: "min-h-screen bg-gray-100 py-8" },
h("h1", { class: "text-4xl font-extrabold text-center mb-8" }, pageTitle),
h(Level2, { sectionTitle: "Primary Section", isVisible: true }),
h(Level2, { sectionTitle: "Secondary Section", isVisible: true })
);
const ShallowTree = () => h(Level1, { pageTitle: "Shallow Tree Benchmark" });
// DeepTree
const Wrapper = ({ depth, maxDepth, children }) => {
const percentage = (depth / maxDepth) * 100;
return h(
"div",
{
class: `depth-${depth}`,
"data-testid": `level-${depth}`,
style: { paddingLeft: "2px", borderLeft: "1px solid rgba(0,0,0,0.1)" },
},
h("span", { class: "text-xs text-gray-400" }, `Level ${depth} (${percentage.toFixed(0)}%)`),
children
);
};
const renderDepth = (current, max) => {
if (current >= max) {
return h(
"div",
{ class: "leaf-node bg-green-100 p-2 rounded" },
h("strong", null, "Leaf Node"),
h("p", { class: "text-sm" }, `Reached depth ${current}`)
);
}
return h(Wrapper, { depth: current, maxDepth: max }, renderDepth(current + 1, max));
};
const DeepTree50 = () => renderDepth(0, 50);
// WideTree
const Card = ({ id, title, description, price, rating, inStock }) =>
h(
"article",
{ class: `border rounded-lg p-4 shadow-sm ${!inStock ? "opacity-50" : ""}` },
h(
"div",
{ class: "flex justify-between items-start mb-2" },
h("h3", { class: "font-semibold text-lg" }, title),
h("span", { class: "text-xs bg-gray-100 px-2 py-1 rounded" }, `#${id}`)
),
h("p", { class: "text-gray-600 text-sm mb-3" }, description),
h(
"div",
{ class: "flex justify-between items-center" },
h("span", { class: "text-xl font-bold text-green-600" }, `$${price.toFixed(2)}`),
h(
"div",
{ class: "flex items-center gap-1" },
h("span", { class: "text-yellow-500" }, "★"),
h("span", { class: "text-sm" }, rating.toFixed(1))
)
),
h(
"div",
{ class: "mt-2" },
inStock
? h("span", { class: "text-green-500 text-sm" }, "In Stock")
: h("span", { class: "text-red-500 text-sm" }, "Out of Stock")
)
);
const WideTree100 = () => {
const items = Array.from({ length: 100 }, (_, i) => ({
id: i + 1,
title: `Product ${i + 1}`,
description: `Description for product ${i + 1}.`,
price: 9.99 + (i % 100),
rating: 3.0 + (i % 20) / 10.0,
inStock: i % 7 !== 0,
}));
return h(
"div",
{ class: "grid grid-cols-4 gap-4 p-4" },
items.map((item) => h(Card, { key: item.id, ...item }))
);
};
// Table
const Table100 = () => {
const users = Array.from({ length: 100 }, (_, i) => ({
id: i + 1,
name: `User ${i + 1}`,
email: `user${i + 1}@example.com`,
role: ["Engineer", "Designer", "Manager"][i % 3],
}));
return h(
"table",
{ class: "min-w-full divide-y divide-gray-200" },
h(
"thead",
{ class: "bg-gray-50" },
h(
"tr",
null,
["ID", "Name", "Email", "Role"].map((header) =>
h(
"th",
{ key: header, class: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase" },
header
)
)
)
),
h(
"tbody",
{ class: "bg-white divide-y divide-gray-200" },
users.map((user, i) =>
h(
"tr",
{ key: user.id, class: i % 2 === 0 ? "bg-white" : "bg-gray-50" },
h("td", { class: "px-6 py-4 text-sm" }, user.id),
h("td", { class: "px-6 py-4 text-sm" }, user.name),
h("td", { class: "px-6 py-4 text-sm" }, user.email),
h("td", { class: "px-6 py-4 text-sm" }, user.role)
)
)
)
);
};
const preactScenarios = {
trivial: { component: Trivial, name: "Trivial" },
shallow: { component: ShallowTree, name: "Shallow Tree" },
deep50: { component: DeepTree50, name: "Deep Tree 50" },
wide100: { component: WideTree100, name: "Wide Tree 100" },
table100: { component: Table100, name: "Table 100" },
};
// Routes
app.get("/", (req, res) => {
const scenarioName = req.query.scenario || "table100";
const scenario = preactScenarios[scenarioName];
if (!scenario) {
return res.status(404).send(`Unknown scenario: ${scenarioName}`);
}
const html = renderToString(h(scenario.component));
res.setHeader("Content-Type", "text/html");
res.send(`<!DOCTYPE html><html><head><title>Benchmark</title></head><body><div id="root">${html}</div></body></html>`);
});
app.get("/health", (req, res) => {
res.json({ status: "ok", framework: "preact", pid: process.pid });
});
app.get("/scenarios", (req, res) => {
const list = Object.entries(preactScenarios).map(([key, val]) => ({
key,
name: val.name,
}));
res.json(list);
});
app.listen(PORT, () => {
console.log(`[preact] Server listening on http://localhost:${PORT}`);
console.log(`[preact] PID: ${process.pid}`);
});
================================================
FILE: benchmark/frameworks/render-bench.ts
================================================
/**
* Pure Render Benchmark - No HTTP
* Comparable to streaming_bench.ml
*/
if (process.env.NODE_ENV !== "production") {
console.error(
"[render-bench] ERROR: NODE_ENV is not 'production' (got " +
JSON.stringify(process.env.NODE_ENV) +
").\n" +
"React's development build is slower than production. Re-run with:\n" +
" NODE_ENV=production bun render-bench.ts\n" +
" NODE_ENV=production node render-bench-node.mjs\n"
);
process.exit(1);
}
import React from "react";
import ReactDOMServer from "react-dom/server";
import * as scenarios from "./shared/scenarios.jsx";
const ITERATIONS = 100;
function measureTimeUs(fn: () => string): [string, number] {
const start = performance.now();
const result = fn();
const end = performance.now();
return [result, (end - start) * 1000]; // Convert ms to µs
}
function formatTimeUs(us: number): string {
if (us < 1000) return `${us.toFixed(2)}µs`;
if (us < 1_000_000) return `${(us / 1000).toFixed(2)}ms`;
return `${(us / 1_000_000).toFixed(2)}s`;
}
interface Result {
name: string;
avgTimeUs: number;
outputBytes: number;
throughputMbS: number;
}
function benchmark(name: string, component: React.ComponentType): Result {
let totalTime = 0;
let outputBytes = 0;
for (let i = 0; i < ITERATIONS; i++) {
const [html, timeUs] = measureTimeUs(() =>
ReactDOMServer.renderToString(React.createElement(component))
);
totalTime += timeUs;
outputBytes = html.length;
}
const avgTime = totalTime / ITERATIONS;
const throughput = (outputBytes / 1_000_000) / (avgTime / 1_000_000);
return {
name,
avgTimeUs: avgTime,
outputBytes,
throughputMbS: throughput,
};
}
const runtime =
typeof (globalThis as any).Bun !== "undefined"
? `Bun ${(globalThis as any).Bun.version}`
: `Node ${process.versions.node}`;
console.log(`Pure Render Benchmark (${runtime} + React, NODE_ENV=production)`);
console.log(`Iterations per scenario: ${ITERATIONS}\n`);
// Keep this list in sync with benchmark/streaming/streaming_bench.ml so each
const testScenarios = [
"trivial",
"shallow",
"deep10",
"deep50",
"wide10",
"wide100",
"wide500",
"table10",
"table100",
"table500",
"propsSmall",
"propsMedium",
"ecommerce24",
"ecommerce48",
"dashboard",
"blog50",
"form",
];
const results: Result[] = [];
for (const key of testScenarios) {
const scenario = scenarios.scenarios[key as keyof typeof scenarios.scenarios];
if (scenario) {
const result = benchmark(key, scenario.component);
results.push(result);
console.log(`${key}: ${formatTimeUs(result.avgTimeUs)} (${result.outputBytes}B)`);
}
}
console.log("\n" + "=".repeat(70));
console.log("COMPARISON TABLE");
console.log("=".repeat(70));
console.log(`${"Scenario".padEnd(20)} ${"Time".padStart(12)} ${"Size".padStart(10)} ${"Throughput".padStart(12)}`);
console.log("-".repeat(70));
for (const r of results) {
console.log(
`${r.name.padEnd(20)} ${formatTimeUs(r.avgTimeUs).padStart(12)} ${(r.outputBytes + "B").padStart(10)} ${(r.throughputMbS.toFixed(1) + "MB/s").padStart(12)}`
);
}
console.log("=".repeat(70));
================================================
FILE: benchmark/frameworks/shared/Blog.jsx
================================================
// Port of benchmark/scenarios/Blog.re
// Purpose: content-heavy page rendering with nested comments
import React from "react";
import { cx } from "./cx.js";
const authors = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"];
const avatars = ["A", "B", "C", "D", "E", "F"];
const generateComments = (count, depth) =>
Array.from({ length: count }, (_, i) => ({
id: i + 1,
author: authors[i % authors.length],
avatar: avatars[i % avatars.length],
content: `This is comment #${i + 1}. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.`,
date: `${1 + (i % 24)} hours ago`,
likes: (i * 3) % 50,
replies:
depth > 0
? Array.from({ length: i % 3 }, (_, j) => ({
id: i * 100 + j,
author: authors[(i + j + 1) % authors.length],
avatar: avatars[(i + j + 1) % avatars.length],
content: `Reply to comment #${i + 1}. Great point!`,
date: `${5 + j * 10} minutes ago`,
likes: j * 2,
replies: [],
}))
: [],
}));
const CommentComponent = ({ comment, depth }) => (
<div
className={cx([
"py-4",
depth > 0
? "ml-12 border-l-2 border-gray-100 pl-4"
: "border-b border-gray-100",
])}
>
<div className="flex items-start gap-4">
<div className="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center flex-shrink-0">
<span className="text-lg">{comment.avatar}</span>
</div>
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<span className="font-semibold text-gray-900">{comment.author}</span>
<span className="text-sm text-gray-500">{comment.date}</span>
</div>
<p className="text-gray-700 mb-3">{comment.content}</p>
<div className="flex items-center gap-4 text-sm">
<button className="text-gray-500 hover:text-blue-600 flex items-center gap-1">
<span>👍</span>
<span>{comment.likes}</span>
</button>
<button className="text-gray-500 hover:text-blue-600">Reply</button>
<button className="text-gray-500 hover:text-blue-600">Share</button>
</div>
{comment.replies.length > 0 && (
<div className="mt-4">
{comment.replies.map((reply) => (
<CommentComponent key={String(reply.id)} comment={reply} depth={depth + 1} />
))}
</div>
)}
</div>
</div>
</div>
);
const ArticleContent = () => (
<article className="prose prose-lg max-w-none">
<p className="text-xl text-gray-600 leading-relaxed mb-8">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.
</p>
<h2 className="text-2xl font-bold text-gray-900 mt-8 mb-4">Introduction</h2>
<p className="text-gray-700 mb-4">
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<p className="text-gray-700 mb-4">
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
</p>
<blockquote className="border-l-4 border-blue-500 pl-4 py-2 my-6 bg-blue-50 rounded-r">
<p className="text-gray-700 italic">
"Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt."
</p>
<cite className="text-sm text-gray-500">— Famous Author</cite>
</blockquote>
<h2 className="text-2xl font-bold text-gray-900 mt-8 mb-4">Key Concepts</h2>
<p className="text-gray-700 mb-4">
Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.
</p>
<ul className="list-disc list-inside space-y-2 mb-6">
<li className="text-gray-700">Ut enim ad minima veniam, quis nostrum exercitationem</li>
<li className="text-gray-700">Corporis suscipit laboriosam, nisi ut aliquid ex ea commodi</li>
<li className="text-gray-700">Quis autem vel eum iure reprehenderit qui in ea voluptate</li>
<li className="text-gray-700">At vero eos et accusamus et iusto odio dignissimos ducimus</li>
</ul>
<div className="bg-gray-100 rounded-lg p-6 my-6">
<h3 className="font-bold text-gray-900 mb-2">💡 Pro Tip</h3>
<p className="text-gray-700">
Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae.
</p>
</div>
<h2 className="text-2xl font-bold text-gray-900 mt-8 mb-4">Code Example</h2>
<pre className="bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto mb-6">
<code>{`let example = () => {
let value = computeValue();
let result = transform(value);
process(result);
};`}</code>
</pre>
<h2 className="text-2xl font-bold text-gray-900 mt-8 mb-4">Conclusion</h2>
<p className="text-gray-700 mb-4">
Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.
</p>
</article>
);
const Sidebar = () => {
const relatedPosts = [
"Understanding Server-Side Rendering",
"The Future of Web Development",
"Performance Optimization Tips",
"Building Scalable Applications",
"Modern JavaScript Frameworks",
];
const tags = ["React", "SSR", "Performance", "JavaScript", "OCaml", "Web Development", "Tutorial"];
return (
<aside className="w-80 flex-shrink-0">
<div className="sticky top-8 space-y-8">
<div className="bg-white rounded-xl p-6 shadow-sm">
<div className="flex items-center gap-4 mb-4">
<div className="w-16 h-16 rounded-full bg-gradient-to-br from-blue-500 to-purple-500 flex items-center justify-center">
<span className="text-2xl">👨</span>
</div>
<div>
<h3 className="font-bold text-gray-900">John Developer</h3>
<p className="text-sm text-gray-500">Senior Engineer</p>
</div>
</div>
<p className="text-gray-600 text-sm mb-4">
Writing about web development, performance, and the joy of coding. 10+ years of experience building things for the web.
</p>
<button className="w-full py-2 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700">
Follow
</button>
</div>
<div className="bg-white rounded-xl p-6 shadow-sm">
<h3 className="font-bold text-gray-900 mb-4">Related Posts</h3>
<ul className="space-y-3">
{relatedPosts.map((title, i) => (
<li key={String(i)}>
<a href="#" className="text-gray-700 hover:text-blue-600 text-sm">{title}</a>
</li>
))}
</ul>
</div>
<div className="bg-white rounded-xl p-6 shadow-sm">
<h3 className="font-bold text-gray-900 mb-4">Tags</h3>
<div className="flex flex-wrap gap-2">
{tags.map((tag, i) => (
<a
key={String(i)}
href={`/tag/${tag.toLowerCase()}`}
className="px-3 py-1 bg-gray-100 text-gray-700 rounded-full text-sm hover:bg-gray-200"
>
{tag}
</a>
))}
</div>
</div>
<div className="bg-gradient-to-br from-blue-600 to-purple-600 rounded-xl p-6 text-white">
<h3 className="font-bold mb-2">📧 Newsletter</h3>
<p className="text-sm text-blue-100 mb-4">Get weekly insights delivered to your inbox.</p>
<input
type="email"
placeholder="your@email.com"
className="w-full px-4 py-2 rounded-lg text-gray-900 mb-3"
/>
<button className="w-full py-2 bg-white text-blue-600 rounded-lg font-medium hover:bg-gray-100">
Subscribe
</button>
</div>
</div>
</aside>
);
};
const CommentsSection = ({ comments }) => (
<section className="mt-12">
<h2 className="text-2xl font-bold text-gray-900 mb-6">{`Comments (${comments.length})`}</h2>
<div className="bg-gray-50 rounded-xl p-6 mb-8">
<h3 className="font-medium text-gray-900 mb-4">Leave a comment</h3>
<textarea
rows={4}
placeholder="Share your thoughts..."
className="w-full px-4 py-3 border border-gray-200 rounded-lg resize-none"
/>
<div className="flex justify-end mt-4">
<button className="px-6 py-2 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700">
Post Comment
</button>
</div>
</div>
<div>
{comments.map((comment) => (
<CommentComponent key={String(comment.id)} comment={comment} depth={0} />
))}
</div>
<div className="mt-8 text-center">
<button className="px-6 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50">
Load more comments
</button>
</div>
</section>
);
const navItems = ["Articles", "Tutorials", "Podcast", "About"];
const shareButtons = [
["🐦", "Twitter"],
["📘", "Facebook"],
["💼", "LinkedIn"],
["📋", "Copy Link"],
];
const Page = ({ commentCount }) => {
const comments = generateComments(commentCount, 1);
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Blog Post - Understanding Server-Side Rendering</title>
</head>
<body className="bg-gray-50">
<header className="bg-white border-b border-gray-200">
<div className="container mx-auto px-4 py-4">
<nav className="flex items-center justify-between">
<a href="/" className="text-2xl font-bold text-gray-900">TechBlog</a>
<div className="flex items-center gap-6">
{navItems.map((item) => (
<a key={item} href="#" className="text-gray-600 hover:text-gray-900">{item}</a>
))}
<button className="px-4 py-2 bg-blue-600 text-white rounded-lg font-medium">
Subscribe
</button>
</div>
</nav>
</div>
</header>
<div className="bg-gradient-to-br from-blue-900 to-purple-900 text-white py-16">
<div className="container mx-auto px-4">
<div className="max-w-3xl">
<div className="flex items-center gap-2 mb-4">
<span className="px-3 py-1 bg-blue-500 rounded-full text-sm font-medium">Tutorial</span>
<span className="text-blue-200">• 15 min read</span>
</div>
<h1 className="text-4xl md:text-5xl font-bold mb-4">
Understanding Server-Side Rendering in Modern Web Applications
</h1>
<p className="text-xl text-blue-100 mb-6">
A comprehensive guide to SSR, its benefits, and how to implement it effectively in your projects.
</p>
<div className="flex items-center gap-4">
<div className="w-12 h-12 rounded-full bg-white/20 flex items-center justify-center">
<span className="text-xl">👨</span>
</div>
<div>
<p className="font-medium">John Developer</p>
<p className="text-sm text-blue-200">Published on November 15, 2024</p>
</div>
</div>
</div>
</div>
</div>
<main className="container mx-auto px-4 py-12">
<div className="flex gap-12">
<div className="flex-1 max-w-3xl">
<div className="flex items-center gap-4 mb-8 pb-8 border-b border-gray-200">
<span className="text-gray-500 text-sm">Share:</span>
{shareButtons.map(([icon, label]) => (
<button
key={label}
className="p-2 bg-gray-100 rounded-full hover:bg-gray-200"
aria-label={label}
>
{icon}
</button>
))}
</div>
<ArticleContent />
<CommentsSection comments={comments} />
</div>
<Sidebar />
</div>
</main>
<footer className="bg-gray-900 text-white py-12">
<div className="container mx-auto px-4 text-center">
<p className="text-gray-400">© 2024 TechBlog. All rights reserved.</p>
</div>
</footer>
</body>
</html>
);
};
export const Blog10 = () => <Page commentCount={10} />;
export const Blog50 = () => <Page commentCount={50} />;
export const Blog100 = () => <Page commentCount={100} />;
================================================
FILE: benchmark/frameworks/shared/Dashboard.jsx
================================================
// Port of benchmark/scenarios/Dashboard.re
// Purpose: admin/dashboard UI rendering
import React from "react";
import { cx } from "./cx.js";
const StatCard = ({ title, value, change, icon, trend }) => {
const trendColor =
trend > 0.0 ? "text-green-500" : trend < 0.0 ? "text-red-500" : "text-gray-500";
const trendIcon = trend > 0.0 ? "↑" : trend < 0.0 ? "↓" : "→";
return (
<div className="bg-white rounded-xl p-6 shadow-sm">
<div className="flex items-center justify-between mb-4">
<span className="text-2xl">{icon}</span>
<span
className={cx([
"text-sm font-medium flex items-center gap-1",
trendColor,
])}
>
{trendIcon}
{`${Math.abs(trend).toFixed(1)}%`}
</span>
</div>
<div>
<h3 className="text-gray-500 text-sm font-medium">{title}</h3>
<p className="text-3xl font-bold text-gray-900 mt-1">{value}</p>
<p className="text-sm text-gray-500 mt-1">{change}</p>
</div>
</div>
);
};
const ChartPlaceholder = ({ title, height }) => (
<div className="bg-white rounded-xl p-6 shadow-sm">
<h3 className="text-lg font-semibold text-gray-900 mb-4">{title}</h3>
<div
className="bg-gradient-to-br from-gray-50 to-gray-100 rounded-lg flex items-center justify-center"
style={{ height: `${height}px` }}
>
<div className="text-center">
<div className="text-4xl mb-2">📊</div>
<p className="text-gray-500 text-sm">Chart visualization</p>
</div>
</div>
</div>
);
const ActivityItem = ({ user, action, target, time, avatar }) => (
<div className="flex items-start gap-4 py-4 border-b border-gray-100 last:border-0">
<div className="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center flex-shrink-0">
<span className="text-lg">{avatar}</span>
</div>
<div className="flex-1 min-w-0">
<p className="text-sm text-gray-900">
<span className="font-medium">{user}</span>
{" "}
{action}
{" "}
<span className="font-medium text-blue-600">{target}</span>
</p>
<p className="text-xs text-gray-500 mt-1">{time}</p>
</div>
</div>
);
const ActivityFeed = () => {
const activities = [
["Alice Chen", "updated", "Marketing Campaign Q4", "2 minutes ago", "👩"],
["Bob Smith", "commented on", "Product Roadmap 2024", "15 minutes ago", "👨"],
["Carol Davis", "completed", "User Research Report", "1 hour ago", "👩"],
["David Kim", "created", "New Feature Proposal", "2 hours ago", "👨"],
["Eve Johnson", "approved", "Budget Request #127", "3 hours ago", "👩"],
["Frank Wilson", "assigned", "Bug Fix #892", "4 hours ago", "🔧"],
["Grace Lee", "reviewed", "Code PR #456", "5 hours ago", "👩"],
["Henry Brown", "deployed", "v2.4.1 Release", "6 hours ago", "🚀"],
];
return (
<div className="bg-white rounded-xl shadow-sm">
<div className="p-6 border-b border-gray-100">
<h3 className="text-lg font-semibold text-gray-900">Recent Activity</h3>
</div>
<div className="px-6">
{activities.map(([user, action, target, time, avatar], i) => (
<ActivityItem
key={String(i)}
user={user}
action={action}
target={target}
time={time}
avatar={avatar}
/>
))}
</div>
<div className="p-4 border-t border-gray-100">
<button className="text-sm text-blue-600 hover:text-blue-700 font-medium">
View all activity →
</button>
</div>
</div>
);
};
const TopPerformers = () => {
const performers = [
{ name: "Sarah Johnson", role: "Sales Lead", metric: 156, avatar: "👩" },
{ name: "Mike Chen", role: "Account Executive", metric: 142, avatar: "👨" },
{ name: "Emily Davis", role: "Sales Rep", metric: 128, avatar: "👩" },
{ name: "James Wilson", role: "Sales Rep", metric: 115, avatar: "👨" },
{ name: "Lisa Brown", role: "Account Executive", metric: 108, avatar: "👩" },
];
return (
<div className="bg-white rounded-xl shadow-sm">
<div className="p-6 border-b border-gray-100">
<h3 className="text-lg font-semibold text-gray-900">Top Performers</h3>
</div>
<div className="p-6">
<div className="space-y-4">
{performers.map((p, i) => (
<div key={String(i)} className="flex items-center gap-4">
<span className="text-lg text-gray-400 w-6">{`#${i + 1}`}</span>
<div className="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center">
<span className="text-lg">{p.avatar}</span>
</div>
<div className="flex-1">
<p className="font-medium text-gray-900">{p.name}</p>
<p className="text-sm text-gray-500">{p.role}</p>
</div>
<div className="text-right">
<p className="font-bold text-gray-900">{p.metric}</p>
<p className="text-xs text-gray-500">deals</p>
</div>
</div>
))}
</div>
</div>
</div>
);
};
const QuickActions = () => {
const actions = [
["📝", "New Report", "bg-blue-100 text-blue-600"],
["👥", "Add User", "bg-green-100 text-green-600"],
["📧", "Send Email", "bg-purple-100 text-purple-600"],
["📊", "Export Data", "bg-orange-100 text-orange-600"],
["⚙️", "Settings", "bg-gray-100 text-gray-600"],
["❓", "Get Help", "bg-yellow-100 text-yellow-600"],
];
return (
<div className="bg-white rounded-xl shadow-sm p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Quick Actions</h3>
<div className="grid grid-cols-3 gap-4">
{actions.map(([icon, label, colors], i) => (
<button
key={String(i)}
className={cx([
"flex flex-col items-center p-4 rounded-xl transition-colors",
colors,
"hover:opacity-80",
])}
>
<span className="text-2xl mb-2">{icon}</span>
<span className="text-sm font-medium">{label}</span>
</button>
))}
</div>
</div>
);
};
const Sidebar = ({ currentPath }) => {
const menuItems = [
["🏠", "Dashboard", "/"],
["📊", "Analytics", "/analytics"],
["👥", "Users", "/users"],
["📦", "Products", "/products"],
["🛒", "Orders", "/orders"],
["💰", "Revenue", "/revenue"],
["📈", "Reports", "/reports"],
["⚙️", "Settings", "/settings"],
];
return (
<aside className="w-64 bg-gray-900 text-white min-h-screen flex-shrink-0">
<div className="p-6">
<h1 className="text-xl font-bold">📊 Dashboard</h1>
</div>
<nav className="px-4">
{menuItems.map(([icon, label, path], i) => (
<a
key={String(i)}
href={path}
className={cx([
"flex items-center gap-3 px-4 py-3 rounded-lg mb-1 transition-colors",
path === currentPath
? "bg-blue-600 text-white"
: "text-gray-400 hover:bg-gray-800 hover:text-white",
])}
>
<span className="text-lg">{icon}</span>
<span className="font-medium">{label}</span>
</a>
))}
</nav>
<div className="absolute bottom-0 left-0 w-64 p-4 border-t border-gray-800">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-full bg-gray-700 flex items-center justify-center">
<span>👤</span>
</div>
<div>
<p className="font-medium text-sm">Admin User</p>
<p className="text-xs text-gray-400">admin@company.com</p>
</div>
</div>
</div>
</aside>
);
};
const Header = () => (
<header className="bg-white border-b border-gray-200 px-8 py-4">
<div className="flex items-center justify-between">
<div>
<h2 className="text-2xl font-bold text-gray-900">Dashboard Overview</h2>
<p className="text-gray-500 text-sm">Welcome back! Here's what's happening.</p>
</div>
<div className="flex items-center gap-4">
<div className="relative">
<input
type="search"
placeholder="Search..."
className="pl-10 pr-4 py-2 border border-gray-200 rounded-lg text-sm w-64"
/>
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">🔍</span>
</div>
<button className="p-2 text-gray-500 hover:text-gray-700 relative">
🔔
<span className="absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full" />
</button>
<button className="p-2 text-gray-500 hover:text-gray-700">⚙️</button>
</div>
</div>
</header>
);
export const Dashboard = () => (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Analytics Dashboard</title>
</head>
<body className="bg-gray-100">
<div className="flex">
<Sidebar currentPath="/" />
<div className="flex-1">
<Header />
<main className="p-8">
<div className="grid grid-cols-4 gap-6 mb-8">
<StatCard title="Total Revenue" value="$284,532" change="vs last month" icon="💰" trend={12.5} />
<StatCard title="Active Users" value="14,832" change="vs last month" icon="👥" trend={8.2} />
<StatCard title="Total Orders" value="3,427" change="vs last month" icon="📦" trend={-2.4} />
<StatCard title="Conversion Rate" value="3.24%" change="vs last month" icon="📈" trend={0.8} />
</div>
<div className="grid grid-cols-2 gap-6 mb-8">
<ChartPlaceholder title="Revenue Over Time" height={300} />
<ChartPlaceholder title="User Growth" height={300} />
</div>
<div className="grid grid-cols-3 gap-6">
<div className="col-span-2">
<ActivityFeed />
</div>
<div className="space-y-6">
<TopPerformers />
<QuickActions />
</div>
</div>
</main>
</div>
</div>
</body>
</html>
);
================================================
FILE: benchmark/frameworks/shared/Ecommerce.jsx
================================================
// Port of benchmark/scenarios/Ecommerce.re
// Purpose: real-world SSR performance for e-commerce page
import React from "react";
import { cx } from "./cx.js";
const brands = ["Apple", "Samsung", "Sony", "LG", "Nike", "Adidas", "Microsoft", "Google"];
const categories = ["Electronics", "Clothing", "Home", "Sports", "Books", "Toys"];
const tagOptions = ["Best Seller", "New", "Sale", "Limited", "Trending", "Eco-Friendly"];
const generateProducts = (count) =>
Array.from({ length: count }, (_, i) => {
const id = i + 1;
const hasDiscount = i % 3 === 0;
const basePrice = 19.99 + (i % 500);
return {
id,
name: `Product ${id} - Premium Edition`,
brand: brands[i % brands.length],
price: hasDiscount ? basePrice * 0.8 : basePrice,
originalPrice: hasDiscount ? basePrice : null,
rating: 3.0 + (i % 21) / 10.0,
reviewCount: 10 + (i % 5000),
image: `https://picsum.photos/seed/${id}/300/300`,
category: categories[i % categories.length],
tags: Array.from({ length: 1 + (i % 3) }, (_, j) => tagOptions[(i + j) % tagOptions.length]),
inStock: i % 10 !== 0,
freeShipping: i % 4 === 0,
prime: i % 2 === 0,
};
});
const StarRating = ({ rating, count }) => {
const fullStars = Math.trunc(rating);
const hasHalfStar = rating - fullStars >= 0.5;
return (
<div className="flex items-center gap-1">
<div className="flex text-yellow-400">
{Array.from({ length: 5 }, (_, i) => (
<span key={String(i)}>
{i < fullStars ? "★" : i === fullStars && hasHalfStar ? "⯨" : "☆"}
</span>
))}
</div>
<span className="text-sm text-gray-500">
{`${rating.toFixed(1)} (${count})`}
</span>
</div>
);
};
const PriceBadge = ({ price, originalPrice }) => (
<div className="flex items-baseline gap-2">
<span className="text-2xl font-bold text-gray-900">{`$${price.toFixed(2)}`}</span>
{originalPrice !== null && (
<>
<span className="text-sm text-gray-500 line-through">{`$${originalPrice.toFixed(2)}`}</span>
<span className="text-sm font-medium text-red-600">
{`${Math.round((1.0 - price / originalPrice) * 100)}% off`}
</span>
</>
)}
</div>
);
const ProductCard = ({ product }) => (
<article className="group relative bg-white rounded-xl shadow-sm hover:shadow-xl transition-all duration-300 overflow-hidden">
<div className="aspect-square overflow-hidden bg-gray-100">
<img
src={product.image}
alt={product.name}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
loading="lazy"
/>
<div className="absolute top-2 left-2 flex flex-col gap-1">
{product.tags.map((tag, i) => (
<span
key={String(i)}
className="px-2 py-1 text-xs font-medium bg-black/70 text-white rounded"
>
{tag}
</span>
))}
</div>
<button
className="absolute top-2 right-2 p-2 bg-white/90 rounded-full hover:bg-white transition-colors"
aria-label="Add to wishlist"
>
♡
</button>
</div>
<div className="p-4">
<div className="mb-1">
<span className="text-xs font-medium text-gray-500 uppercase tracking-wide">
{product.brand}
</span>
</div>
<h3 className="text-sm font-medium text-gray-900 mb-2 line-clamp-2">
<a href={`/product/${product.id}`} className="hover:text-blue-600">
{product.name}
</a>
</h3>
<StarRating rating={product.rating} count={product.reviewCount} />
<div className="mt-2">
<PriceBadge price={product.price} originalPrice={product.originalPrice} />
</div>
<div className="mt-2 flex items-center gap-2 text-xs">
{product.prime && <span className="text-blue-600 font-medium">✓ Prime</span>}
{product.freeShipping && <span className="text-green-600">Free Shipping</span>}
</div>
<div className="mt-3">
{product.inStock ? (
<span className="text-sm text-green-600">In Stock</span>
) : (
<span className="text-sm text-red-600">Out of Stock</span>
)}
</div>
<button
className={cx([
"mt-3 w-full py-2 px-4 rounded-lg font-medium transition-colors",
product.inStock
? "bg-yellow-400 hover:bg-yellow-500 text-gray-900"
: "bg-gray-200 text-gray-500 cursor-not-allowed",
])}
disabled={!product.inStock}
>
{product.inStock ? "Add to Cart" : "Unavailable"}
</button>
</div>
</article>
);
const FilterSidebar = () => {
const priceRanges = [
["Under $25", 25],
["$25 - $50", 50],
["$50 - $100", 100],
["$100 - $200", 200],
["Over $200", 999],
];
const filterCategories = ["Electronics", "Clothing", "Home", "Sports", "Books", "Toys"];
const filterBrands = ["Apple", "Samsung", "Sony", "LG", "Nike", "Adidas", "Microsoft", "Google"];
return (
<aside className="w-64 flex-shrink-0">
<div className="sticky top-4 space-y-6">
<div className="bg-white rounded-lg p-4 shadow-sm">
<h3 className="font-medium text-gray-900 mb-3">Category</h3>
<div className="space-y-2">
{filterCategories.map((cat) => (
<label key={cat} className="flex items-center gap-2 cursor-pointer">
<input type="checkbox" className="rounded text-blue-600" />
<span className="text-sm text-gray-700">{cat}</span>
</label>
))}
</div>
</div>
<div className="bg-white rounded-lg p-4 shadow-sm">
<h3 className="font-medium text-gray-900 mb-3">Price</h3>
<div className="space-y-2">
{priceRanges.map(([label]) => (
<label key={label} className="flex items-center gap-2 cursor-pointer">
<input type="radio" name="price" className="text-blue-600" />
<span className="text-sm text-gray-700">{label}</span>
</label>
))}
</div>
</div>
<div className="bg-white rounded-lg p-4 shadow-sm">
<h3 className="font-medium text-gray-900 mb-3">Rating</h3>
<div className="space-y-2">
{Array.from({ length: 4 }, (_, i) => {
const stars = 4 - i;
return (
<label key={String(stars)} className="flex items-center gap-2 cursor-pointer">
<input type="checkbox" className="rounded text-blue-600" />
<span className="text-yellow-400">{"★".repeat(stars)}</span>
<span className="text-sm text-gray-500">& Up</span>
</label>
);
})}
</div>
</div>
<div className="bg-white rounded-lg p-4 shadow-sm">
<h3 className="font-medium text-gray-900 mb-3">Brand</h3>
<div className="space-y-2">
{filterBrands.map((brand) => (
<label key={brand} className="flex items-center gap-2 cursor-pointer">
<input type="checkbox" className="rounded text-blue-600" />
<span className="text-sm text-gray-700">{brand}</span>
</label>
))}
</div>
</div>
</div>
</aside>
);
};
const ProductGrid = ({ products }) => (
<div className="flex-1">
<div className="mb-4 flex items-center justify-between">
<p className="text-sm text-gray-600">{`Showing ${products.length} results`}</p>
<select className="px-3 py-2 border rounded-lg text-sm">
<option>Sort by: Featured</option>
<option>Price: Low to High</option>
<option>Price: High to Low</option>
<option>Avg. Customer Review</option>
<option>Newest Arrivals</option>
</select>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{products.map((product) => (
<ProductCard key={String(product.id)} product={product} />
))}
</div>
<nav className="mt-8 flex items-center justify-center gap-2">
<button className="px-4 py-2 border rounded-lg text-sm hover:bg-gray-50">Previous</button>
{Array.from({ length: 5 }, (_, i) => (
<button
key={String(i)}
className={cx([
"px-4 py-2 rounded-lg text-sm",
i === 0 ? "bg-blue-600 text-white" : "border hover:bg-gray-50",
])}
>
{i + 1}
</button>
))}
<span className="px-2">...</span>
<button className="px-4 py-2 border rounded-lg text-sm hover:bg-gray-50">20</button>
<button className="px-4 py-2 border rounded-lg text-sm hover:bg-gray-50">Next</button>
</nav>
</div>
);
const Header = () => (
<header className="bg-gray-900 text-white">
<div className="container mx-auto px-4">
<div className="py-2 text-xs border-b border-gray-700 flex justify-between">
<div className="flex gap-4">
<a href="#" className="hover:text-gray-300">Help</a>
<a href="#" className="hover:text-gray-300">Track Order</a>
</div>
<div className="flex gap-4">
<a href="#" className="hover:text-gray-300">Sell on Store</a>
<a href="#" className="hover:text-gray-300">Language: EN</a>
</div>
</div>
<div className="py-4 flex items-center gap-8">
<a href="/" className="text-2xl font-bold">STORE</a>
<div className="flex-1 max-w-2xl">
<div className="flex">
<select className="px-3 py-2 bg-gray-100 text-gray-900 rounded-l-lg border-r text-sm">
<option>All Categories</option>
</select>
<input
type="search"
placeholder="Search products..."
className="flex-1 px-4 py-2 text-gray-900"
/>
<button className="px-6 py-2 bg-yellow-400 text-gray-900 rounded-r-lg font-medium hover:bg-yellow-500">
Search
</button>
</div>
</div>
<div className="flex items-center gap-6">
<a href="/account" className="flex flex-col items-center text-xs hover:text-gray-300">
<span className="text-lg">👤</span>
Account
</a>
<a href="/orders" className="flex flex-col items-center text-xs hover:text-gray-300">
<span className="text-lg">📦</span>
Orders
</a>
<a href="/cart" className="flex flex-col items-center text-xs hover:text-gray-300 relative">
<span className="text-lg">🛒</span>
Cart
<span className="absolute -top-1 -right-1 bg-yellow-400 text-gray-900 text-xs rounded-full w-5 h-5 flex items-center justify-center font-medium">
3
</span>
</a>
</div>
</div>
<nav className="py-2 flex gap-6 text-sm">
{["Today's Deals", "Customer Service", "Registry", "Gift Cards", "Sell"].map((item) => (
<a key={item} href="#" className="hover:text-gray-300">{item}</a>
))}
</nav>
</div>
</header>
);
const footerSections = [
["Get to Know Us", ["Careers", "Blog", "About", "Investor Relations"]],
["Make Money with Us", ["Sell products", "Become an Affiliate", "Advertise", "Self-Publish"]],
["Payment Products", ["Business Card", "Shop with Points", "Reload Balance", "Currency Converter"]],
["Let Us Help You", ["Your Account", "Returns Centre", "Recalls", "Help"]],
];
const Page = ({ products }) => (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Shop - Products</title>
</head>
<body className="bg-gray-100 min-h-screen">
<Header />
<div className="container mx-auto px-4 py-4">
<nav className="text-sm text-gray-500">
<a href="/" className="hover:text-gray-700">Home</a>
{" / "}
<a href="/products" className="hover:text-gray-700">Products</a>
{" / "}
<span className="text-gray-900">All</span>
</nav>
</div>
<main className="container mx-auto px-4 py-6">
<div className="flex gap-8">
<FilterSidebar />
<ProductGrid products={products} />
</div>
</main>
<footer className="bg-gray-900 text-white mt-12 py-12">
<div className="container mx-auto px-4">
<div className="grid grid-cols-4 gap-8">
{footerSections.map(([title, links]) => (
<div key={title}>
<h4 className="font-medium mb-4">{title}</h4>
<ul className="space-y-2 text-sm text-gray-400">
{links.map((link) => (
<li key={link}>
<a href="#" className="hover:text-white">{link}</a>
</li>
))}
</ul>
</div>
))}
</div>
</div>
</footer>
</body>
</html>
);
// Match the OCaml "module Products24 = { let products = generateProducts(24) }"
// idiom: generate once at module load, then reuse.
const products24 = generateProducts(24);
const products48 = generateProducts(48);
const products100 = generateProducts(100);
export const Ecommerce24 = () => <Page products={products24} />;
export const Ecommerce48 = () => <Page products={products48} />;
export const Ecommerce100 = () => <Page products={products100} />;
================================================
FILE: benchmark/frameworks/shared/Form.jsx
================================================
// Port of benchmark/scenarios/Form.re
// Purpose: form-heavy page rendering
import React from "react";
import { cx } from "./cx.js";
const FormField = ({
id,
label,
type = "text",
required = true,
placeholder = "",
helpText,
error,
}) => {
let ariaDescribedby;
if (helpText !== undefined) ariaDescribedby = `${id}-help`;
else if (error !== undefined) ariaDescribedby = `${id}-error`;
else ariaDescribedby = "<nop>";
return (
<div className="mb-4">
<label htmlFor={id} className="block text-sm font-medium text-gray-700 mb-1">
{label}
{required && <span className="text-red-500 ml-1">*</span>}
</label>
<input
type={type}
id={id}
name={id}
required={required}
placeholder={placeholder}
className={cx([
"w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors",
error !== undefined ? "border-red-500 bg-red-50" : "border-gray-300",
])}
aria-describedby={ariaDescribedby}
aria-invalid={error !== undefined ? "true" : "false"}
/>
{helpText !== undefined && (
<p id={`${id}-help`} className="mt-1 text-sm text-gray-500">{helpText}</p>
)}
{error !== undefined && (
<p id={`${id}-error`} className="mt-1 text-sm text-red-600 flex items-center gap-1">
<span>⚠️</span>
{error}
</p>
)}
</div>
);
};
const SelectField = ({ id, label, options, required = true }) => (
<div className="mb-4">
<label htmlFor={id} className="block text-sm font-medium text-gray-700 mb-1">
{label}
{required && <span className="text-red-500 ml-1">*</span>}
</label>
<select
id={id}
name={id}
required={required}
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="">Select an option...</option>
{options.map(([value, optLabel], i) => (
<option key={String(i)} value={value}>{optLabel}</option>
))}
</select>
</div>
);
const TextareaField = ({ id, label, rows = 4, required = true, placeholder = "" }) => (
<div className="mb-4">
<label htmlFor={id} className="block text-sm font-medium text-gray-700 mb-1">
{label}
{required && <span className="text-red-500 ml-1">*</span>}
</label>
<textarea
id={id}
name={id}
rows={rows}
required={required}
placeholder={placeholder}
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none"
/>
</div>
);
const CheckboxField = ({ id, label, description }) => (
<div className="mb-4">
<div className="flex items-start gap-3">
<input
type="checkbox"
id={id}
name={id}
className="mt-1 h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
/>
<div>
<label htmlFor={id} className="text-sm font-medium text-gray-700">{label}</label>
{description !== undefined && (
<p className="text-sm text-gray-500">{description}</p>
)}
</div>
</div>
</div>
);
const RadioGroup = ({ name, label, options }) => (
<fieldset className="mb-4">
<legend className="block text-sm font-medium text-gray-700 mb-2">{label}</legend>
<div className="space-y-2">
{options.map(([value, optLabel, description], i) => (
<div key={String(i)} className="flex items-start gap-3">
<input
type="radio"
id={`${name}-${value}`}
name={name}
value={value}
className="mt-1 h-4 w-4 text-blue-600 border-gray-300 focus:ring-blue-500"
/>
<div>
<label htmlFor={`${name}-${value}`} className="text-sm font-medium text-gray-700">
{optLabel}
</label>
{description !== undefined && description !== null && (
<p className="text-sm text-gray-500">{description}</p>
)}
</div>
</div>
))}
</div>
</fieldset>
);
const FormSection = ({ title, description, children }) => (
<div className="mb-8 pb-8 border-b border-gray-200 last:border-0">
<h3 className="text-lg font-semibold text-gray-900 mb-1">{title}</h3>
{description !== undefined && (
<p className="text-sm text-gray-500 mb-4">{description}</p>
)}
{children}
</div>
);
const ProgressSteps = ({ steps, currentStep }) => (
<nav className="mb-8">
<ol className="flex items-center">
{steps.map(([label], i) => {
const isComplete = i < currentStep;
const isCurrent = i === currentStep;
return (
<li key={String(i)} className="flex items-center">
<div className="flex items-center">
<span
className={cx([
"w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium",
isComplete
? "bg-blue-600 text-white"
: isCurrent
? "border-2 border-blue-600 text-blue-600"
: "border-2 border-gray-300 text-gray-500",
])}
>
{isComplete ? "✓" : i + 1}
</span>
<span
className={cx([
"ml-2 text-sm font-medium",
isCurrent ? "text-blue-600" : "text-gray-500",
])}
>
{label}
</span>
</div>
{i < steps.length - 1 && (
<div
className={cx([
"w-16 h-0.5 mx-4",
isComplete ? "bg-blue-600" : "bg-gray-300",
])}
/>
)}
</li>
);
})}
</ol>
</nav>
);
const PersonalInfoForm = () => (
<FormSection title="Personal Information" description="Please provide your basic personal details.">
<div className="grid grid-cols-2 gap-4">
<FormField id="firstName" label="First Name" placeholder="John" />
<FormField id="lastName" label="Last Name" placeholder="Doe" />
</div>
<FormField id="email" label="Email Address" type="email" placeholder="john@example.com" />
<FormField id="phone" label="Phone Number" type="tel" placeholder="+1 (555) 000-0000" />
<FormField id="dob" label="Date of Birth" type="date" helpText="You must be at least 18 years old" />
<SelectField
id="gender"
label="Gender"
required={false}
options={[
["male", "Male"],
["female", "Female"],
["other", "Other"],
["prefer-not", "Prefer not to say"],
]}
/>
</FormSection>
);
const AddressForm = () => (
<FormSection title="Address Information" description="Your residential address for correspondence.">
<FormField id="address1" label="Address Line 1" placeholder="123 Main Street" />
<FormField id="address2" label="Address Line 2" required={false} placeholder="Apt 4B" />
<div className="grid grid-cols-2 gap-4">
<FormField id="city" label="City" placeholder="New York" />
<FormField id="state" label="State/Province" placeholder="NY" />
</div>
<div className="grid grid-cols-2 gap-4">
<FormField id="zip" label="ZIP/Postal Code" placeholder="10001" />
<SelectField
id="country"
label="Country"
options={[
["us", "United States"],
["ca", "Canada"],
["uk", "United Kingdom"],
["de", "Germany"],
["fr", "France"],
["jp", "Japan"],
["au", "Australia"],
]}
/>
</div>
</FormSection>
);
const EmploymentForm = () => (
<FormSection title="Employment Information" description="Tell us about your current employment.">
<RadioGroup
name="employmentStatus"
label="Employment Status"
options={[
["employed", "Employed", "Currently working full-time or part-time"],
["self-employed", "Self-Employed", "Running your own business"],
["unemployed", "Unemployed", "Currently seeking employment"],
["student", "Student", "Full-time student"],
["retired", "Retired", null],
]}
/>
<FormField id="employer" label="Employer Name" required={false} placeholder="Company Inc." />
<FormField id="jobTitle" label="Job Title" required={false} placeholder="Software Engineer" />
<SelectField
id="industry"
label="Industry"
required={false}
options={[
["tech", "Technology"],
["finance", "Finance"],
["healthcare", "Healthcare"],
["education", "Education"],
["retail", "Retail"],
["manufacturing", "Manufacturing"],
["other", "Other"],
]}
/>
<FormField
id="income"
label="Annual Income"
type="number"
required={false}
placeholder="50000"
helpText="This information helps us provide better recommendations"
/>
</FormSection>
);
const PreferencesForm = () => (
<FormSection title="Preferences" description="Customize your experience.">
<SelectField
id="language"
label="Preferred Language"
options={[
["en", "English"],
["es", "Spanish"],
["fr", "French"],
["de", "German"],
["zh", "Chinese"],
["ja", "Japanese"],
]}
/>
<SelectField
id="timezone"
label="Timezone"
options={[
["pst", "Pacific Time (PT)"],
["mst", "Mountain Time (MT)"],
["cst", "Central Time (CT)"],
["est", "Eastern Time (ET)"],
["utc", "UTC"],
["gmt", "GMT"],
]}
/>
<RadioGroup
name="theme"
label="Theme Preference"
options={[
["light", "Light", "Best for daytime use"],
["dark", "Dark", "Easier on the eyes at night"],
["auto", "System", "Follows your device settings"],
]}
/>
<div className="mt-6">
<h4 className="text-sm font-medium text-gray-700 mb-3">Notification Preferences</h4>
<CheckboxField id="notifyEmail" label="Email notifications" description="Receive updates via email" />
<CheckboxField id="notifySms" label="SMS notifications" description="Receive text message alerts" />
<CheckboxField id="notifyPush" label="Push notifications" description="Receive browser notifications" />
<CheckboxField id="newsletter" label="Newsletter subscription" description="Weekly digest of updates and tips" />
</div>
</FormSection>
);
const AdditionalInfoForm = () => (
<FormSection title="Additional Information">
<TextareaField
id="bio"
label="Bio"
rows={4}
required={false}
placeholder="Tell us a bit about yourself..."
/>
<FormField id="website" label="Website" type="url" required={false} placeholder="https://example.com" />
<FormField
id="linkedin"
label="LinkedIn Profile"
type="url"
required={false}
placeholder="https://linkedin.com/in/username"
/>
<FormField id="twitter" label="Twitter Handle" required={false} placeholder="@username" />
<TextareaField
id="referral"
label="How did you hear about us?"
rows={2}
required={false}
placeholder="Friend, social media, search engine, etc."
/>
</FormSection>
);
const TermsForm = () => (
<FormSection title="Terms and Conditions">
<div className="bg-gray-50 rounded-lg p-4 mb-4 h-48 overflow-y-auto text-sm text-gray-600">
<p className="mb-2">By using our services, you agree to these terms. Please read them carefully.</p>
<p className="mb-2">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
<p className="mb-2">
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<p>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
</p>
</div>
<CheckboxField id="acceptTerms" label="I accept the Terms and Conditions" description="You must accept to continue" />
<CheckboxField id="acceptPrivacy" label="I accept the Privacy Policy" description="Your data will be handled according to our privacy policy" />
<CheckboxField id="acceptMarketing" label="I agree to receive marketing communications" description="Optional - you can unsubscribe at any time" />
</FormSection>
);
const formSteps = [
["Personal", "Basic info"],
["Address", "Location"],
["Employment", "Work details"],
["Preferences", "Settings"],
["Additional", "Extra info"],
["Review", "Confirm"],
];
export const Form = () => (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Registration Form</title>
</head>
<body className="bg-gray-100 min-h-screen py-12">
<div className="max-w-3xl mx-auto">
<div className="text-center mb-8">
<h1 className="text-3xl font-bold text-gray-900 mb-2">Create Your Account</h1>
<p className="text-gray-600">Complete the form below to get started</p>
</div>
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<ProgressSteps steps={formSteps} currentStep={2} />
</div>
<form className="bg-white rounded-xl shadow-sm p-8">
<PersonalInfoForm />
<AddressForm />
<EmploymentForm />
<PreferencesForm />
<AdditionalInfoForm />
<TermsForm />
<div className="flex items-center justify-between pt-6 border-t border-gray-200">
<button
type="button"
className="px-6 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50"
>
← Previous
</button>
<div className="flex gap-4">
<button
type="button"
className="px-6 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50"
>
Save Draft
</button>
<button
type="submit"
className="px-8 py-2 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700"
>
Continue →
</button>
</div>
</div>
</form>
<div className="mt-6 text-center text-sm text-gray-500">
<p>
Need help?{" "}
<a href="#" className="text-blue-600 hover:underline">Contact Support</a>
</p>
</div>
</div>
</body>
</html>
);
================================================
FILE: benchmark/frameworks/shared/PropsHeavy.jsx
================================================
// Port of benchmark/scenarios/PropsHeavy.re
// Purpose: test attribute serialization performance
import React from "react";
import { cx } from "./cx.js";
const HeavyDiv = ({ id, children }) => (
<div
id={`heavy-div-${id}`}
className="heavy-component p-4 m-2 bg-white rounded-lg shadow-md border border-gray-200 hover:shadow-lg transition-shadow duration-300"
data-testid={`test-heavy-${id}`}
role="article"
aria-label={`Heavy component number ${id}`}
aria-describedby={`desc-${id}`}
tabIndex={0}
style={{
backgroundColor: "#ffffff",
padding: "16px",
margin: "8px",
borderRadius: "8px",
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
transition: "all 0.3s ease",
cursor: "pointer",
userSelect: "none",
overflow: "hidden",
position: "relative",
zIndex: "1",
}}
>
{children}
</div>
);
const HeavyInput = ({ id, label }) => (
<div className="form-group mb-4">
<label
htmlFor={`input-${id}`}
className="block text-sm font-medium text-gray-700 mb-1"
>
{label}
</label>
<input
type="text"
id={`input-${id}`}
name={`field_${id}`}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm"
placeholder={`Enter ${label}...`}
autoComplete="off"
autoCapitalize="none"
autoCorrect="off"
spellCheck={false}
required
minLength={1}
maxLength={100}
pattern="[A-Za-z0-9]+"
title="Only alphanumeric characters"
aria-label={`Input for ${label}`}
aria-required="true"
data-testid={`input-test-${id}`}
style={{
width: "100%",
padding: "8px 12px",
fontSize: "14px",
lineHeight: "1.5",
borderWidth: "1px",
borderStyle: "solid",
borderColor: "#d1d5db",
borderRadius: "6px",
outline: "none",
}}
/>
</div>
);
const HeavyButton = ({ id, text, variant }) => {
let bgColor, textColor;
switch (variant) {
case "primary":
bgColor = "bg-blue-600";
textColor = "text-white";
break;
case "secondary":
bgColor = "bg-gray-200";
textColor = "text-gray-800";
break;
case "danger":
bgColor = "bg-red-600";
textColor = "text-white";
break;
case "success":
bgColor = "bg-green-600";
textColor = "text-white";
break;
}
return (
<button
type="button"
id={`btn-${id}`}
className={cx([
"inline-flex items-center justify-center px-4 py-2 rounded-md font-medium",
"focus:outline-none focus:ring-2 focus:ring-offset-2",
"disabled:opacity-50 disabled:cursor-not-allowed",
"transition-colors duration-200",
bgColor,
textColor,
])}
disabled={false}
aria-pressed="false"
aria-label={`Button: ${text}`}
aria-describedby={`btn-desc-${id}`}
data-testid={`btn-test-${id}`}
role="button"
tabIndex={0}
style={{
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
padding: "8px 16px",
fontSize: "14px",
fontWeight: "500",
lineHeight: "1.25",
borderRadius: "6px",
border: "none",
cursor: "pointer",
textDecoration: "none",
}}
>
{text}
</button>
);
};
const HeavyTable = ({ rows, cols }) => (
<div className="overflow-x-auto">
<table
className="min-w-full divide-y divide-gray-200"
role="grid"
aria-label="Data table"
aria-rowcount={rows}
aria-colcount={cols}
>
<thead className="bg-gray-50">
<tr role="row">
{Array.from({ length: cols }, (_, col) => (
<th
key={String(col)}
scope="col"
role="columnheader"
aria-sort="none"
aria-colindex={col + 1}
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
style={{
padding: "12px 24px",
textAlign: "left",
fontSize: "12px",
fontWeight: "500",
textTransform: "uppercase",
letterSpacing: "0.05em",
}}
>
{`Column ${col + 1}`}
</th>
))}
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{Array.from({ length: rows }, (_, row) => (
<tr
key={String(row)}
role="row"
aria-rowindex={row + 1}
className={row % 2 === 0 ? "bg-white" : "bg-gray-50"}
>
{Array.from({ length: cols }, (_, col) => (
<td
key={String(col)}
role="gridcell"
aria-colindex={col + 1}
className="px-6 py-4 whitespace-nowrap text-sm text-gray-900"
data-testid={`cell-${row}-${col}`}
style={{
padding: "16px 24px",
whiteSpace: "nowrap",
fontSize: "14px",
}}
>
{`R${row + 1}C${col + 1}`}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
export const PropsSmall = () => (
<div className="p-4">
{Array.from({ length: 10 }, (_, i) => (
<HeavyDiv key={String(i)} id={i}>
<HeavyInput id={i} label={`Field ${i}`} />
<div className="flex gap-2 mt-2">
<HeavyButton id={i * 3} text="Primary" variant="primary" />
<HeavyButton id={i * 3 + 1} text="Secondary" variant="secondary" />
<HeavyButton id={i * 3 + 2} text="Delete" variant="danger" />
</div>
</HeavyDiv>
))}
</div>
);
export const PropsMedium = () => (
<div className="p-4">
{Array.from({ length: 50 }, (_, i) => (
<HeavyDiv key={String(i)} id={i}>
<HeavyInput id={i} label={`Field ${i}`} />
<HeavyButton id={i} text="Submit" variant="primary" />
</HeavyDiv>
))}
</div>
);
export const PropsLarge = () => (
<div className="p-4">
<HeavyTable rows={100} cols={10} />
</div>
);
================================================
FILE: benchmark/frameworks/shared/cx.js
================================================
// Matches benchmark/scenarios/Cx.re exactly:
// let make = cns => cns |> List.filter(x => x != "") |> String.concat(" ") |> String.trim;
export function cx(classes) {
return classes.filter((x) => x !== "").join(" ").trim();
}
================================================
FILE: benchmark/frameworks/shared/scenarios.jsx
================================================
/**
* Shared benchmark scenarios for JavaScript frameworks
* These mirror the Reason/OCaml scenarios for fair comparison
*/
import React from "react";
import { PropsSmall, PropsMedium, PropsLarge } from "./PropsHeavy.jsx";
import { Ecommerce24, Ecommerce48, Ecommerce100 } from "./Ecommerce.jsx";
import { Dashboard } from "./Dashboard.jsx";
import { Blog10, Blog50, Blog100 } from "./Blog.jsx";
import { Form } from "./Form.jsx";
// ============================================================================
// Trivial - Baseline
// ============================================================================
export const Trivial = () => <div>Hello World</div>;
// ============================================================================
// ShallowTree - 5 levels deep with props
// ============================================================================
const Level5 = ({ title, subtitle, active, count }) => (
<div className={`p-4 rounded ${active ? "bg-blue-500" : ""}`}>
<h5 className="font-bold">{title}</h5>
<p className="text-sm text-gray-500">{subtitle}</p>
<span className="badge">{count}</span>
</div>
);
const Level4 = ({ title, description, isHighlighted, itemCount }) => (
<section
className={`mb-4 ${isHighlighted ? "border-l-4 border-blue-500" : ""}`}
>
<Level5
title={title}
subtitle={description}
active={isHighlighted}
count={itemCount}
/>
<Level5
title={`${title} Alt`}
subtitle="Secondary"
active={false}
count={itemCount * 2}
/>
</section>
);
const Level3 = ({ groupName, expanded, totalItems }) => (
<article className={`p-6 ${expanded ? "shadow-lg" : ""}`}>
<h3 className="text-xl font-semibold mb-4">{groupName}</h3>
<Level4
title="First Item"
description="Description A"
isHighlighted={true}
itemCount={totalItems}
/>
<Level4
title="Second Item"
description="Description B"
isHighlighted={false}
itemCount={Math.floor(totalItems / 2)}
/>
</article>
);
const Level2 = ({ sectionTitle, isVisible }) => (
<div className={`container mx-auto ${isVisible ? "block" : ""}`}>
<h2 className="text-2xl font-bold mb-6">{sectionTitle}</h2>
<Level3 groupName="Group Alpha" expanded={true} totalItems={42} />
<Level3 groupName="Group Beta" expanded={false} totalItems={17} />
</div>
);
const Level1 = ({ pageTitle }) => (
<main className="min-h-screen bg-gray-100 py-8">
<h1 className="text-4xl font-extrabold text-center mb-8">{pageTitle}</h1>
<Level2 sectionTitle="Primary Section" isVisible={true} />
<Level2 sectionTitle="Secondary Section" isVisible={true} />
</main>
);
export const ShallowTree = () => <Level1 pageTitle="Shallow Tree Benchmark" />;
// ============================================================================
// DeepTree - 50+ levels deep
// ============================================================================
const Wrapper = ({ depth, maxDepth, children }) => {
const percentage = (depth / maxDepth) * 100;
return (
<div
className={`depth-${depth} border-l pl-0.5`}
data-testid={`level-${depth}`}
>
<span className="text-xs text-gray-400">
Level {depth} ({percentage.toFixed(0)}%)
</span>
{children}
</div>
);
};
const renderDepth = (current, max) => {
if (current >= max) {
return (
<div className="leaf-node bg-green-100 p-2 rounded">
<strong>Leaf Node</strong>
<p className="text-sm">Reached depth {current}</p>
</div>
);
}
return (
<Wrapper depth={current} maxDepth={max}>
{renderDepth(current + 1, max)}
</Wrapper>
);
};
export const DeepTree10 = () => renderDepth(0, 10);
export const DeepTree25 = () => renderDepth(0, 25);
export const DeepTree50 = () => renderDepth(0, 50);
export const DeepTree100 = () => renderDepth(0, 100);
export const DeepTree = DeepTree50;
// ============================================================================
// WideTree - Many siblings
// ============================================================================
const Card = ({ id, title, description, price, rating, inStock }) => (
<article
className={`border rounded-lg p-4 shadow-sm ${!inStock ? "opacity-50" : ""}`}
>
<div className="flex justify-between items-start mb-2">
<h3 className="font-semibold text-lg">{title}</h3>
<span className="text-xs bg-gray-100 px-2 py-1 rounded">#{id}</span>
</div>
<p className="text-gray-600 text-sm mb-3">{description}</p>
<div className="flex justify-between items-center">
<span className="text-xl font-bold text-green-600">
${price.toFixed(2)}
</span>
<div className="flex items-center gap-1">
<span className="text-yellow-500">★</span>
<span className="text-sm">{rating.toFixed(1)}</span>
</div>
</div>
<div className="mt-2">
{inStock ? (
<span className="text-green-500 text-sm">In Stock</span>
) : (
<span className="text-red-500 text-sm">Out of Stock</span>
)}
</div>
</article>
);
const generateItems = (count) =>
Array.from({ length: count }, (_, i) => ({
id: i + 1,
title: `Product ${i + 1}`,
description: `This is the description for product ${i + 1}. It contains useful information.`,
price: 9.99 + (i % 100),
rating: 3.0 + (i % 20) / 10.0,
inStock: i % 7 !== 0,
}));
const WideTreeBase = ({ count, cols }) => {
const items = generateItems(count);
return (
<div className={`grid grid-cols-${cols} gap-4 p-4`}>
{items.map((item) => (
<Card key={item.id} {...item} />
))}
</div>
);
};
export const WideTree10 = () => <WideTreeBase count={10} cols={2} />;
export const WideTree100 = () => <WideTreeBase count={100} cols={4} />;
export const WideTree500 = () => <WideTreeBase count={500} cols={5} />;
export const WideTree1000 = () => <WideTreeBase count={1000} cols={5} />;
export const WideTree = WideTree100;
// ================================================================
gitextract_rs9v3350/ ├── .dockerignore ├── .gitattributes ├── .githooks/ │ └── pre-push ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ ├── benchmark.yml │ ├── ci.yml │ └── docker.yml ├── .gitignore ├── .ocamlformat ├── CHANGES.md ├── Dockerfile ├── LICENSE.md ├── Makefile ├── README.md ├── arch/ │ ├── browser/ │ │ ├── .gitignore │ │ ├── package.json │ │ ├── public/ │ │ │ ├── index.html │ │ │ └── manifest.json │ │ └── src/ │ │ ├── index.css │ │ └── index.js │ └── server/ │ ├── head-ordering.js │ ├── package.json │ ├── react-dom-server-node-dom-props.js │ ├── react-dom-server.js │ ├── render-html-to-stream.js │ ├── render-rsc-to-stream.js │ ├── test-useid-edge-cases.js │ └── test-useid.js ├── benchmark/ │ ├── Makefile │ ├── README.md │ ├── allocation.ml │ ├── bench.ml │ ├── dune │ ├── frameworks/ │ │ ├── bun-native/ │ │ │ └── server.tsx │ │ ├── hono-bun/ │ │ │ └── server.ts │ │ ├── hono-node/ │ │ │ └── server.mjs │ │ ├── node-express/ │ │ │ └── server.mjs │ │ ├── node-fastify/ │ │ │ └── server.mjs │ │ ├── package.json │ │ ├── preact/ │ │ │ └── server.mjs │ │ ├── render-bench.ts │ │ └── shared/ │ │ ├── Blog.jsx │ │ ├── Dashboard.jsx │ │ ├── Ecommerce.jsx │ │ ├── Form.jsx │ │ ├── PropsHeavy.jsx │ │ ├── cx.js │ │ └── scenarios.jsx │ ├── memory/ │ │ ├── dune │ │ └── memory_bench.ml │ ├── native/ │ │ ├── dune │ │ └── server.re │ ├── perf-work/ │ │ ├── PERF_NEXT.md │ │ ├── README.md │ │ ├── alloc-table500.txt │ │ ├── alloc-wide500.txt │ │ ├── alloc_profile.ml │ │ ├── baseline-run1.txt │ │ ├── baseline-run2.txt │ │ ├── baseline-run3.txt │ │ ├── cycles-out/ │ │ │ ├── callgrind-table100.annotate.stderr │ │ │ ├── callgrind-table100.out │ │ │ ├── callgrind-table100.stderr │ │ │ ├── callgrind-table100.stdout │ │ │ ├── callgrind-table100.txt │ │ │ ├── callgrind-wide100.annotate.stderr │ │ │ ├── callgrind-wide100.out │ │ │ ├── callgrind-wide100.stderr │ │ │ ├── callgrind-wide100.stdout │ │ │ ├── callgrind-wide100.txt │ │ │ ├── perf-stat-wide100.stdout │ │ │ └── perf-stat-wide100.txt │ │ ├── diff_styles.ml │ │ ├── dump_html.ml │ │ ├── dune │ │ ├── dune_extra │ │ ├── perf_profile.ml │ │ ├── perf_profile.sh │ │ ├── phase1-run1.txt │ │ ├── phase1-run2.txt │ │ ├── phase2-final.txt │ │ ├── phase2-run1.txt │ │ ├── phase2-run2.txt │ │ ├── phase3-run1.txt │ │ ├── phase3-run2.txt │ │ ├── phase3b-run1.txt │ │ ├── phase3b-run2.txt │ │ ├── phase3c-run1.txt │ │ ├── phase3c-run2.txt │ │ ├── phase4-final.txt │ │ ├── phase4-run1.txt │ │ ├── phase7-final.txt │ │ ├── style_alloc_bench.ml │ │ ├── unification_bench.ml │ │ ├── unified-experiment-runs.txt │ │ └── unified-experiment.txt │ ├── results/ │ │ └── .gitkeep │ ├── runner/ │ │ ├── package.json │ │ ├── runner.mjs │ │ └── visualize.html │ ├── scenarios/ │ │ ├── Blog.re │ │ ├── Cx.re │ │ ├── Dashboard.re │ │ ├── DeepTree.re │ │ ├── Ecommerce.re │ │ ├── Form.re │ │ ├── PropsHeavy.re │ │ ├── ShallowTree.re │ │ ├── Table.re │ │ ├── Trivial.re │ │ ├── WideTree.re │ │ └── dune │ └── streaming/ │ ├── dune │ └── streaming_bench.ml ├── demo/ │ ├── README.md │ ├── client/ │ │ ├── DummyRouterRSC.re │ │ ├── HydrateRoot.re │ │ ├── NestedRouterRSC.re │ │ ├── RenderRoot.re │ │ ├── ServerOnlyRSC.re │ │ ├── SinglePageRSC.re │ │ ├── build.mjs │ │ ├── dune │ │ └── package.json │ ├── dream-nested-router/ │ │ ├── dune │ │ ├── js/ │ │ │ ├── HistoryCache.re │ │ │ ├── HistoryState.re │ │ │ ├── VirtualHistory.re │ │ │ └── dune │ │ ├── native/ │ │ │ ├── README.md │ │ │ ├── RouterRSC.re │ │ │ ├── RouterRSC.rei │ │ │ ├── dune │ │ │ └── shared/ │ │ │ ├── DynamicParams.re │ │ │ ├── NavigationResponse.re │ │ │ ├── Route.re │ │ │ ├── Router.re │ │ │ └── Router.rei │ │ └── test_router_rsc.ml │ ├── dream-rsc/ │ │ ├── DreamRSC.re │ │ ├── DreamRSC.rei │ │ └── dune │ ├── dune │ ├── package.json │ ├── server/ │ │ ├── db/ │ │ │ └── notes.json │ │ ├── dune │ │ ├── pages/ │ │ │ ├── Comments.re │ │ │ ├── DummyRouterRSC.re │ │ │ ├── Home.re │ │ │ ├── NestedRouter.re │ │ │ ├── NoteItem.re │ │ │ ├── NoteList.re │ │ │ ├── ServerOnlyRSC.re │ │ │ ├── SidebarNote.re │ │ │ └── SinglePageRSC.re │ │ └── server.re │ ├── styles.css │ ├── tailwind.config.js │ └── universal/ │ ├── js/ │ │ ├── Dream.re │ │ └── dune │ └── native/ │ ├── DB.re │ ├── Date.re │ ├── FunctionReferences.re │ ├── FunctionReferences.rei │ ├── Markdown.re │ ├── SidebarNote.re │ ├── dune │ └── shared/ │ ├── Align.re │ ├── App.re │ ├── Arrow.re │ ├── Button.re │ ├── Context.re │ ├── Counter.re │ ├── Cx.re │ ├── Debug_props.re │ ├── DeleteNoteButton.re │ ├── DemoLayout.re │ ├── Document.re │ ├── DummyClientRouter.re │ ├── Expander.re │ ├── GlobalStyles.re │ ├── Hr.re │ ├── InputText.re │ ├── Link.re │ ├── NestedRouter_CreateNoteButton.re │ ├── NestedRouter_DeleteNoteButton.re │ ├── NestedRouter_EditButton.re │ ├── NestedRouter_NoteEditor.re │ ├── NestedRouter_NoteItem.re │ ├── NestedRouter_NoteList.re │ ├── NestedRouter_SearchField.re │ ├── NestedRouter_SidebarNote.re │ ├── NestedRouter_SidebarNoteContent.re │ ├── Note.re │ ├── NoteEditor.re │ ├── NoteListSkeleton.re │ ├── NotePreview.re │ ├── NoteSkeleton.re │ ├── Promise_renderer.re │ ├── RR.re │ ├── RequestContextDemo.re │ ├── Routes.re │ ├── Row.re │ ├── SearchField.re │ ├── ServerActionFromPropsClient.re │ ├── ServerActionWithError.re │ ├── ServerActionWithFormData.re │ ├── ServerActionWithFormDataFormAction.re │ ├── ServerActionWithFormDataServer.re │ ├── ServerActionWithFormDataWithArg.re │ ├── ServerActionWithOptionalArg.re │ ├── ServerActionWithSimpleResponse.re │ ├── ServerFunctions.re │ ├── SidebarNoteContent.re │ ├── Spinner.re │ ├── Stack.re │ ├── Static_small.re │ ├── Text.re │ ├── Textarea.re │ └── Theme.re ├── documentation/ │ ├── browser_ppx.mld │ ├── dune │ ├── externals-melange-attributes.mld │ ├── get-started.mld │ ├── how-to-organise-universal-code.mld │ ├── index.mld │ ├── ssr-and-hydration.mld │ └── universal-code.mld ├── dune ├── dune-project ├── fly.toml ├── packages/ │ ├── Belt/ │ │ ├── src/ │ │ │ ├── Belt.re │ │ │ ├── Belt_Array.ml │ │ │ ├── Belt_Array.mli │ │ │ ├── Belt_Float.ml │ │ │ ├── Belt_Float.mli │ │ │ ├── Belt_HashMap.ml │ │ │ ├── Belt_HashMap.mli │ │ │ ├── Belt_HashMapInt.ml │ │ │ ├── Belt_HashMapInt.mli │ │ │ ├── Belt_HashMapString.ml │ │ │ ├── Belt_HashMapString.mli │ │ │ ├── Belt_HashSet.ml │ │ │ ├── Belt_HashSet.mli │ │ │ ├── Belt_HashSetInt.ml │ │ │ ├── Belt_HashSetInt.mli │ │ │ ├── Belt_HashSetString.ml │ │ │ ├── Belt_HashSetString.mli │ │ │ ├── Belt_Id.ml │ │ │ ├── Belt_Id.mli │ │ │ ├── Belt_Int.ml │ │ │ ├── Belt_Int.mli │ │ │ ├── Belt_List.ml │ │ │ ├── Belt_List.mli │ │ │ ├── Belt_Map.ml │ │ │ ├── Belt_Map.mli │ │ │ ├── Belt_MapDict.ml │ │ │ ├── Belt_MapDict.mli │ │ │ ├── Belt_MapInt.ml │ │ │ ├── Belt_MapInt.mli │ │ │ ├── Belt_MapString.ml │ │ │ ├── Belt_MapString.mli │ │ │ ├── Belt_MutableMap.ml │ │ │ ├── Belt_MutableMap.mli │ │ │ ├── Belt_MutableMapInt.ml │ │ │ ├── Belt_MutableMapInt.mli │ │ │ ├── Belt_MutableMapString.ml │ │ │ ├── Belt_MutableMapString.mli │ │ │ ├── Belt_MutableQueue.ml │ │ │ ├── Belt_MutableQueue.mli │ │ │ ├── Belt_MutableSet.ml │ │ │ ├── Belt_MutableSet.mli │ │ │ ├── Belt_MutableSetInt.ml │ │ │ ├── Belt_MutableSetInt.mli │ │ │ ├── Belt_MutableSetString.ml │ │ │ ├── Belt_MutableSetString.mli │ │ │ ├── Belt_MutableStack.ml │ │ │ ├── Belt_MutableStack.mli │ │ │ ├── Belt_Option.ml │ │ │ ├── Belt_Option.mli │ │ │ ├── Belt_Range.ml │ │ │ ├── Belt_Range.mli │ │ │ ├── Belt_Result.ml │ │ │ ├── Belt_Result.mli │ │ │ ├── Belt_Set.ml │ │ │ ├── Belt_Set.mli │ │ │ ├── Belt_SetDict.ml │ │ │ ├── Belt_SetDict.mli │ │ │ ├── Belt_SetInt.ml │ │ │ ├── Belt_SetInt.mli │ │ │ ├── Belt_SetString.ml │ │ │ ├── Belt_SetString.mli │ │ │ ├── Belt_SortArray.ml │ │ │ ├── Belt_SortArray.mli │ │ │ ├── Belt_SortArrayInt.ml │ │ │ ├── Belt_SortArrayInt.mli │ │ │ ├── Belt_SortArrayString.ml │ │ │ ├── Belt_SortArrayString.mli │ │ │ ├── Belt_internalAVLset.ml │ │ │ ├── Belt_internalAVLset.mli │ │ │ ├── Belt_internalAVLtree.ml │ │ │ ├── Belt_internalAVLtree.mli │ │ │ ├── Belt_internalBuckets.ml │ │ │ ├── Belt_internalBuckets.mli │ │ │ ├── Belt_internalBucketsType.ml │ │ │ ├── Belt_internalBucketsType.mli │ │ │ ├── Belt_internalMapInt.ml │ │ │ ├── Belt_internalMapString.ml │ │ │ ├── Belt_internalSetBuckets.ml │ │ │ ├── Belt_internalSetBuckets.mli │ │ │ ├── Belt_internalSetInt.ml │ │ │ ├── Belt_internalSetString.ml │ │ │ ├── caml_hash.ml │ │ │ ├── dune │ │ │ └── stubs.c │ │ └── test/ │ │ ├── Test_Belt_Array.ml │ │ ├── Test_Belt_Float.ml │ │ ├── Test_Belt_HashMap.ml │ │ ├── Test_Belt_HashMap_Int.ml │ │ ├── Test_Belt_HashMap_String.ml │ │ ├── Test_Belt_HashSet_Int.ml │ │ ├── Test_Belt_HashSet_String.ml │ │ ├── Test_Belt_Int.ml │ │ ├── Test_Belt_List.ml │ │ ├── Test_Belt_Map.ml │ │ ├── Test_Belt_Map_Dict.ml │ │ ├── Test_Belt_Map_Int.ml │ │ ├── Test_Belt_Map_String.ml │ │ ├── Test_Belt_MutableMap.ml │ │ ├── Test_Belt_MutableMap_Int.ml │ │ ├── Test_Belt_MutableMap_String.ml │ │ ├── Test_Belt_MutableQueue.ml │ │ ├── Test_Belt_MutableSet.ml │ │ ├── Test_Belt_MutableSet_Int.ml │ │ ├── Test_Belt_MutableSet_String.ml │ │ ├── Test_Belt_MutableStack.ml │ │ ├── Test_Belt_Option.ml │ │ ├── Test_Belt_Result.ml │ │ ├── Test_Belt_Set.ml │ │ ├── Test_Belt_Set_Dict.ml │ │ ├── Test_Belt_Set_Int.ml │ │ ├── Test_Belt_Set_String.ml │ │ ├── Test_Belt_SortArray.ml │ │ ├── Test_Belt_SortArray_Int.ml │ │ ├── Test_Belt_SortArray_String.ml │ │ ├── Test_Belt_Support.ml │ │ ├── benchmark.ml │ │ ├── dune │ │ └── test.ml │ ├── Dom/ │ │ ├── Dom.ml │ │ ├── Dom_storage.ml │ │ └── dune │ ├── Js/ │ │ ├── lib/ │ │ │ ├── Js.ml │ │ │ ├── Js.mli │ │ │ ├── Js_array.ml │ │ │ ├── Js_array.mli │ │ │ ├── Js_bigint.ml │ │ │ ├── Js_bigint.mli │ │ │ ├── Js_console.ml │ │ │ ├── Js_console.mli │ │ │ ├── Js_date.ml │ │ │ ├── Js_date.mli │ │ │ ├── Js_dict.ml │ │ │ ├── Js_dict.mli │ │ │ ├── Js_exn.ml │ │ │ ├── Js_exn.mli │ │ │ ├── Js_float.ml │ │ │ ├── Js_float.mli │ │ │ ├── Js_formdata.ml │ │ │ ├── Js_formdata.mli │ │ │ ├── Js_global.ml │ │ │ ├── Js_global.mli │ │ │ ├── Js_int.ml │ │ │ ├── Js_int.mli │ │ │ ├── Js_internal.ml │ │ │ ├── Js_internal.mli │ │ │ ├── Js_json.ml │ │ │ ├── Js_json.mli │ │ │ ├── Js_map.ml │ │ │ ├── Js_map.mli │ │ │ ├── Js_math.ml │ │ │ ├── Js_math.mli │ │ │ ├── Js_null.ml │ │ │ ├── Js_null.mli │ │ │ ├── Js_nullable.ml │ │ │ ├── Js_nullable.mli │ │ │ ├── Js_obj.ml │ │ │ ├── Js_obj.mli │ │ │ ├── Js_promise.ml │ │ │ ├── Js_promise.mli │ │ │ ├── Js_re.ml │ │ │ ├── Js_re.mli │ │ │ ├── Js_set.ml │ │ │ ├── Js_set.mli │ │ │ ├── Js_string.ml │ │ │ ├── Js_string.mli │ │ │ ├── Js_typed_array.ml │ │ │ ├── Js_typed_array.mli │ │ │ ├── Js_typed_array2.ml │ │ │ ├── Js_typed_array2.mli │ │ │ ├── Js_types.ml │ │ │ ├── Js_types.mli │ │ │ ├── Js_undefined.ml │ │ │ ├── Js_undefined.mli │ │ │ ├── Js_vector.ml │ │ │ ├── Js_vector.mli │ │ │ ├── Js_weakmap.ml │ │ │ ├── Js_weakmap.mli │ │ │ ├── Js_weakset.ml │ │ │ ├── Js_weakset.mli │ │ │ └── dune │ │ └── test/ │ │ ├── bigint_tests/ │ │ │ ├── arithmetic.ml │ │ │ ├── as_int_n.ml │ │ │ ├── as_uint_n.ml │ │ │ ├── bitwise.ml │ │ │ ├── comparison.ml │ │ │ ├── constructor.ml │ │ │ ├── conversion.ml │ │ │ └── prototype.ml │ │ ├── date_tests/ │ │ │ ├── getters.ml │ │ │ ├── local_getters.ml │ │ │ ├── now.ml │ │ │ ├── parse.ml │ │ │ ├── setters.ml │ │ │ ├── to_iso_string.ml │ │ │ ├── to_string.ml │ │ │ └── utc.ml │ │ ├── dune │ │ ├── helpers.ml │ │ ├── number_tests/ │ │ │ ├── is_finite.ml │ │ │ ├── is_integer.ml │ │ │ ├── is_nan.ml │ │ │ ├── parse_float.ml │ │ │ ├── parse_int.ml │ │ │ ├── to_exponential.ml │ │ │ ├── to_precision.ml │ │ │ └── to_string.ml │ │ ├── regexp_tests/ │ │ │ ├── dotall.ml │ │ │ ├── named_groups.ml │ │ │ └── unicode.ml │ │ ├── string_tests/ │ │ │ ├── normalize.ml │ │ │ └── search.ml │ │ ├── test.ml │ │ └── undefined_tests/ │ │ └── undefined.ml │ ├── browser-ppx/ │ │ ├── dune │ │ ├── ppx.ml │ │ └── tests/ │ │ ├── at_browser_only.t │ │ ├── at_platform.t │ │ ├── dune │ │ ├── pexp_apply.t │ │ ├── pexp_constraint_re.t │ │ ├── pexp_fun.t │ │ ├── pexp_fun_with_vb.t │ │ ├── pexp_function.t │ │ ├── pexp_ident.t │ │ ├── playground.t/ │ │ │ ├── input.re │ │ │ └── run.t │ │ ├── preprocess.t │ │ ├── standalone.ml │ │ ├── structure_item.t │ │ ├── structure_item_re.t │ │ ├── switch-platform.t/ │ │ │ ├── input.re │ │ │ └── run.t │ │ └── use_effect.t │ ├── esbuild-plugin/ │ │ ├── dune │ │ ├── extract_client_components.ml │ │ ├── package.json │ │ ├── plugin.mjs │ │ └── test/ │ │ ├── ClientComponent.js │ │ ├── ClientComponentWithModule.js │ │ ├── ServerFunction.js │ │ ├── dune │ │ └── run.t │ ├── expand-styles-attribute/ │ │ ├── dune │ │ ├── expand_styles_attribute.ml │ │ └── test/ │ │ ├── dune │ │ └── test.ml │ ├── fetch/ │ │ ├── Fetch.ml │ │ └── dune │ ├── html/ │ │ ├── Html.ml │ │ └── dune │ ├── melange.ppx/ │ │ ├── base32/ │ │ │ ├── LICENSES/ │ │ │ │ └── ISC.txt │ │ │ ├── README.md │ │ │ └── lib/ │ │ │ ├── base32.ml │ │ │ ├── base32.mli │ │ │ └── dune │ │ ├── derive_util.ml │ │ ├── double_hash.ml │ │ ├── dune │ │ ├── get_set.ml │ │ ├── js_converter.ml │ │ ├── js_properties.ml │ │ ├── pipe_first.ml │ │ ├── ppx.ml │ │ ├── regex.ml │ │ ├── tests/ │ │ │ ├── dune │ │ │ ├── external.t │ │ │ ├── input.ml │ │ │ ├── jsConverter.t │ │ │ ├── jsProperties.t │ │ │ ├── mel_as.t │ │ │ ├── mel_module.t │ │ │ ├── mel_obj.t │ │ │ ├── mel_raw.t │ │ │ ├── mel_send.t │ │ │ ├── mel_send_pipe.t │ │ │ ├── pipe_first.t/ │ │ │ │ ├── input.ml │ │ │ │ └── run.t │ │ │ ├── private.t │ │ │ ├── regex.t/ │ │ │ │ ├── input.ml │ │ │ │ └── run.t │ │ │ ├── standalone.ml │ │ │ └── string_interpolation.t │ │ └── xxhash/ │ │ ├── XXH64.ml │ │ ├── dune │ │ └── test_xxh64.ml │ ├── promise/ │ │ ├── js/ │ │ │ ├── dune │ │ │ ├── promise.re │ │ │ └── promise.rei │ │ └── native/ │ │ ├── dune │ │ ├── promise.re │ │ └── promise.rei │ ├── react/ │ │ ├── src/ │ │ │ ├── React.ml │ │ │ ├── React.mli │ │ │ ├── ReactEvent.ml │ │ │ ├── ReasonReactRouter.ml │ │ │ ├── ReasonReactRouter.mli │ │ │ └── dune │ │ └── test/ │ │ ├── dune │ │ ├── test.ml │ │ ├── test_cloneElement.ml │ │ └── test_react.ml │ ├── react-server-dom-esbuild/ │ │ ├── ReactServerDOMEsbuild.js │ │ ├── ReactServerDOMEsbuild.re │ │ ├── dune │ │ └── package.json │ ├── reactDom/ │ │ ├── src/ │ │ │ ├── Push_stream.ml │ │ │ ├── ReactDOM.ml │ │ │ ├── ReactDOM.mli │ │ │ ├── ReactDOMStyle.ml │ │ │ ├── ReactDOMStyle.mli │ │ │ ├── ReactServerDOM.ml │ │ │ ├── ReactServerDOM.mli │ │ │ └── dune │ │ └── test/ │ │ ├── dune │ │ ├── test.ml │ │ ├── test_RSC_decoders.ml │ │ ├── test_RSC_html.ml │ │ ├── test_RSC_html_shell.ml │ │ ├── test_RSC_model.ml │ │ ├── test_reactDOMStyle.ml │ │ ├── test_renderToStaticMarkup.ml │ │ ├── test_renderToStream.ml │ │ ├── test_renderToString.ml │ │ ├── test_useId.ml │ │ └── test_write_to_buffer.ml │ ├── rsc/ │ │ ├── README.md │ │ ├── js/ │ │ │ ├── RSC.ml │ │ │ ├── RSC.mli │ │ │ └── dune │ │ ├── native/ │ │ │ ├── RSC.ml │ │ │ ├── RSC.mli │ │ │ └── dune │ │ ├── ppx_common/ │ │ │ ├── dune │ │ │ ├── ppx_deriving_tools.ml │ │ │ ├── ppx_deriving_tools.mli │ │ │ └── rsc_deriving_common.ml │ │ ├── ppx_js/ │ │ │ ├── dune │ │ │ └── ppx_deriving_rsc_js.ml │ │ └── ppx_native/ │ │ ├── dune │ │ └── ppx_deriving_rsc_native.ml │ ├── runtime/ │ │ ├── Runtime.ml │ │ ├── Runtime.mli │ │ └── dune │ ├── server-reason-react-ppx/ │ │ ├── DomProps.ml │ │ ├── DomProps.mli │ │ ├── Style_rewrite.ml │ │ ├── cram/ │ │ │ ├── client-component-e2e.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── client-component-no-props.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── client-component-on-the-client-nested.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── client-component-on-the-client.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── client-component-on-the-server.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── client-component-with-fn-error.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── client-props-decoding.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── component-definition-at-toplevel.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── component-definition.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── component-defintion-signatures.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── dune │ │ │ ├── dune-describe-pp.sh │ │ │ ├── ensure-attributes-are-present.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── external.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── functor.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── jsx-fragment.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── locations/ │ │ │ │ ├── input.re │ │ │ │ └── run │ │ │ ├── lower-call-missing-prop.t/ │ │ │ │ ├── input.re │ │ │ │ ├── run.t │ │ │ │ └── wrong-prop.re │ │ │ ├── lower-call-reserved-prop.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── lower-calls.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── ppx.sh │ │ │ ├── reason.expected │ │ │ ├── server-client-props.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── server-function-on-client.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── server-function-on-server.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── shared-folder-prefix-melange.t/ │ │ │ │ ├── js/ │ │ │ │ │ └── input.re │ │ │ │ └── run.t │ │ │ ├── shared-folder-prefix-native.t/ │ │ │ │ ├── native/ │ │ │ │ │ └── input.ml │ │ │ │ └── run.t │ │ │ ├── standalone.ml │ │ │ ├── styles.t/ │ │ │ │ ├── input.re │ │ │ │ └── run.t │ │ │ ├── temp.ml │ │ │ ├── upper-calls-ocaml.t/ │ │ │ │ ├── input.ml │ │ │ │ └── run.t │ │ │ └── upper-calls.t/ │ │ │ ├── input.re │ │ │ └── run.t │ │ ├── dune │ │ ├── server_reason_react_ppx.ml │ │ ├── static_analysis.ml │ │ └── test/ │ │ ├── dune │ │ └── test.re │ ├── url/ │ │ ├── URL.rei │ │ ├── js/ │ │ │ ├── URL.re │ │ │ └── dune │ │ ├── native/ │ │ │ ├── URL.re │ │ │ └── dune │ │ └── test/ │ │ ├── dune │ │ └── test_native.re │ └── webapi/ │ ├── src/ │ │ ├── Canvas/ │ │ │ ├── Webapi__Canvas__Canvas2d.re │ │ │ └── Webapi__Canvas__WebGl.re │ │ ├── Dom/ │ │ │ ├── Webapi__Dom__AnimationEvent.re │ │ │ ├── Webapi__Dom__Attr.re │ │ │ ├── Webapi__Dom__BeforeUnloadEvent.re │ │ │ ├── Webapi__Dom__CdataSection.re │ │ │ ├── Webapi__Dom__CharacterData.re │ │ │ ├── Webapi__Dom__ChildNode.re │ │ │ ├── Webapi__Dom__ClipboardEvent.re │ │ │ ├── Webapi__Dom__CloseEvent.re │ │ │ ├── Webapi__Dom__Comment.re │ │ │ ├── Webapi__Dom__CompositionEvent.re │ │ │ ├── Webapi__Dom__CssStyleDeclaration.re │ │ │ ├── Webapi__Dom__CustomEvent.re │ │ │ ├── Webapi__Dom__Document.re │ │ │ ├── Webapi__Dom__DocumentFragment.re │ │ │ ├── Webapi__Dom__DocumentOrShadowRoot.re │ │ │ ├── Webapi__Dom__DocumentType.re │ │ │ ├── Webapi__Dom__DomImplementation.re │ │ │ ├── Webapi__Dom__DomRect.re │ │ │ ├── Webapi__Dom__DomStringMap.re │ │ │ ├── Webapi__Dom__DomTokenList.re │ │ │ ├── Webapi__Dom__DragEvent.re │ │ │ ├── Webapi__Dom__Element.re │ │ │ ├── Webapi__Dom__ErrorEvent.re │ │ │ ├── Webapi__Dom__Event.re │ │ │ ├── Webapi__Dom__EventTarget.re │ │ │ ├── Webapi__Dom__FocusEvent.re │ │ │ ├── Webapi__Dom__GlobalEventHandlers.re │ │ │ ├── Webapi__Dom__History.re │ │ │ ├── Webapi__Dom__HtmlCollection.re │ │ │ ├── Webapi__Dom__HtmlDocument.re │ │ │ ├── Webapi__Dom__HtmlElement.re │ │ │ ├── Webapi__Dom__HtmlFormElement.re │ │ │ ├── Webapi__Dom__HtmlImageElement.re │ │ │ ├── Webapi__Dom__HtmlInputElement.re │ │ │ ├── Webapi__Dom__IdbVersionChangeEvent.re │ │ │ ├── Webapi__Dom__Image.re │ │ │ ├── Webapi__Dom__InputEvent.re │ │ │ ├── Webapi__Dom__KeyboardEvent.re │ │ │ ├── Webapi__Dom__Location.re │ │ │ ├── Webapi__Dom__MouseEvent.re │ │ │ ├── Webapi__Dom__MutationObserver.re │ │ │ ├── Webapi__Dom__MutationRecord.re │ │ │ ├── Webapi__Dom__NamedNodeMap.re │ │ │ ├── Webapi__Dom__Node.re │ │ │ ├── Webapi__Dom__NodeFilter.re │ │ │ ├── Webapi__Dom__NodeIterator.re │ │ │ ├── Webapi__Dom__NodeList.re │ │ │ ├── Webapi__Dom__NonDocumentTypeChildNode.re │ │ │ ├── Webapi__Dom__NonElementParentNode.re │ │ │ ├── Webapi__Dom__PageTransitionEvent.re │ │ │ ├── Webapi__Dom__ParentNode.re │ │ │ ├── Webapi__Dom__PointerEvent.re │ │ │ ├── Webapi__Dom__PopStateEvent.re │ │ │ ├── Webapi__Dom__ProcessingInstruction.re │ │ │ ├── Webapi__Dom__ProgressEvent.re │ │ │ ├── Webapi__Dom__Range.re │ │ │ ├── Webapi__Dom__RelatedEvent.re │ │ │ ├── Webapi__Dom__Selection.re │ │ │ ├── Webapi__Dom__ShadowRoot.re │ │ │ ├── Webapi__Dom__Slotable.re │ │ │ ├── Webapi__Dom__StorageEvent.re │ │ │ ├── Webapi__Dom__SvgZoomEvent.re │ │ │ ├── Webapi__Dom__Text.re │ │ │ ├── Webapi__Dom__TimeEvent.re │ │ │ ├── Webapi__Dom__TouchEvent.re │ │ │ ├── Webapi__Dom__TrackEvent.re │ │ │ ├── Webapi__Dom__TransitionEvent.re │ │ │ ├── Webapi__Dom__TreeWalker.re │ │ │ ├── Webapi__Dom__Types.re │ │ │ ├── Webapi__Dom__UiEvent.re │ │ │ ├── Webapi__Dom__ValidityState.re │ │ │ ├── Webapi__Dom__WebGlContextEvent.re │ │ │ ├── Webapi__Dom__WheelEvent.re │ │ │ └── Webapi__Dom__Window.re │ │ ├── ResizeObserver/ │ │ │ └── Webapi__ResizeObserver__ResizeObserverEntry.re │ │ ├── Webapi.re │ │ ├── Webapi__Base64.re │ │ ├── Webapi__Blob.re │ │ ├── Webapi__Canvas.re │ │ ├── Webapi__Dom.re │ │ ├── Webapi__File.re │ │ ├── Webapi__Performance.re │ │ ├── Webapi__ReadableStream.re │ │ ├── Webapi__ResizeObserver.re │ │ ├── Webapi__Url.re │ │ └── dune │ └── tests/ │ ├── Canvas/ │ │ └── Webapi__Canvas__Canvas2d__test.re │ ├── Dom/ │ │ ├── Webapi__Dom__AnimationEvent__test.re │ │ ├── Webapi__Dom__BeforeUnloadEvent__test.re │ │ ├── Webapi__Dom__ClipboardEvent__test.re │ │ ├── Webapi__Dom__CloseEvent__test.re │ │ ├── Webapi__Dom__CompositionEvent__test.re │ │ ├── Webapi__Dom__CustomEvent__test.re │ │ ├── Webapi__Dom__Document__test.re │ │ ├── Webapi__Dom__DomStringMap__test.re │ │ ├── Webapi__Dom__DomTokenList__test.re │ │ ├── Webapi__Dom__DragEvent__test.re │ │ ├── Webapi__Dom__Element__test.re │ │ ├── Webapi__Dom__ErrorEvent__test.re │ │ ├── Webapi__Dom__EventTarget__test.re │ │ ├── Webapi__Dom__Event__test.re │ │ ├── Webapi__Dom__FocusEvent__test.re │ │ ├── Webapi__Dom__GlobalEventHandlers__test.re │ │ ├── Webapi__Dom__History__test.re │ │ ├── Webapi__Dom__HtmlDocument__test.re │ │ ├── Webapi__Dom__HtmlElement__test.re │ │ ├── Webapi__Dom__HtmlFormElement__test.re │ │ ├── Webapi__Dom__IdbVersionChangeEvent__test.re │ │ ├── Webapi__Dom__Image__test.re │ │ ├── Webapi__Dom__InputEvent__test.re │ │ ├── Webapi__Dom__KeyboardEvent__test.re │ │ ├── Webapi__Dom__Location__test.re │ │ ├── Webapi__Dom__MouseEvent__test.re │ │ ├── Webapi__Dom__NodeList__test.re │ │ ├── Webapi__Dom__Node__test.re │ │ ├── Webapi__Dom__PageTransitionEvent__test.re │ │ ├── Webapi__Dom__PointerEvent__test.re │ │ ├── Webapi__Dom__PopStateEvent__test.re │ │ ├── Webapi__Dom__ProgressEvent__test.re │ │ ├── Webapi__Dom__Range__test.re │ │ ├── Webapi__Dom__RelatedEvent__test.re │ │ ├── Webapi__Dom__Selection__test.re │ │ ├── Webapi__Dom__StorageEvent__test.re │ │ ├── Webapi__Dom__SvgZoomEvent__test.re │ │ ├── Webapi__Dom__Text__test.re │ │ ├── Webapi__Dom__TimeEvent__test.re │ │ ├── Webapi__Dom__TouchEvent__test.re │ │ ├── Webapi__Dom__TrackEvent__test.re │ │ ├── Webapi__Dom__TransitionEvent__test.re │ │ ├── Webapi__Dom__UiEvent__test.re │ │ ├── Webapi__Dom__WebGlContextEvent__test.re │ │ ├── Webapi__Dom__WheelEvent__test.re │ │ └── Webapi__Dom__Window__test.re │ ├── Webapi__Base64__test.re │ ├── Webapi__Blob__test.re │ ├── Webapi__File__test.re │ ├── Webapi__Performace__test.re │ ├── Webapi__ReadableStream__test.re │ ├── Webapi__ResizeObserver__test.re │ ├── Webapi__Url__test.re │ ├── _dune │ └── testHelpers.re ├── server-reason-react.opam └── server-reason-react.opam.template
SYMBOL INDEX (91 symbols across 20 files)
FILE: arch/server/head-ordering.js
function render (line 23) | function render(element, label) {
function main (line 49) | async function main() {
FILE: arch/server/react-dom-server.js
function App (line 105) | function App() {
FILE: arch/server/test-useid-edge-cases.js
function DivWithId (line 4) | function DivWithId({ label }) {
function SuspenseWithUseId (line 11) | function SuspenseWithUseId() {
function FallbackWithId (line 21) | function FallbackWithId() {
function SuspenseWithIdInBoth (line 26) | function SuspenseWithIdInBoth() {
function FragmentTest (line 43) | function FragmentTest() {
function FragmentMultipleChildren (line 56) | function FragmentMultipleChildren() {
function NestedFragments (line 70) | function NestedFragments() {
function NullBetween (line 87) | function NullBetween() {
function ConditionalChildren (line 98) | function ConditionalChildren({ show }) {
function KeyedFragments (line 108) | function KeyedFragments() {
function ManySiblings (line 126) | function ManySiblings() {
function UseIdAfterSuspense (line 134) | function UseIdAfterSuspense() {
function ProviderWithUseId (line 149) | function ProviderWithUseId() {
function KitchenSink (line 158) | function KitchenSink() {
FILE: arch/server/test-useid.js
function DivWithId (line 5) | function DivWithId() {
function DivWithTwoIds (line 11) | function DivWithTwoIds() {
function DivWithThreeIds (line 18) | function DivWithThreeIds() {
function Wrapper (line 30) | function Wrapper({ children }) {
function ParentWithId (line 35) | function ParentWithId({ children }) {
function Test1 (line 41) | function Test1() {
function Test2 (line 46) | function Test2() {
function Test3 (line 56) | function Test3() {
function Test4 (line 65) | function Test4() {
function Test5 (line 70) | function Test5() {
function Test6 (line 75) | function Test6() {
function Test7 (line 89) | function Test7() {
function Test8 (line 102) | function Test8() {
function Test9 (line 111) | function Test9() {
function Test10 (line 122) | function Test10() {
function Test11 (line 141) | function Test11() {
function Test12a (line 149) | function Test12a() {
function Test12b (line 152) | function Test12b() {
FILE: benchmark/frameworks/bun-native/server.tsx
constant PORT (line 9) | const PORT = Number(process.env.PORT) || 3005;
method fetch (line 13) | fetch(req) {
FILE: benchmark/frameworks/hono-bun/server.ts
constant PORT (line 11) | const PORT = Number(process.env.PORT) || 3004;
FILE: benchmark/frameworks/hono-node/server.mjs
constant PORT (line 12) | const PORT = process.env.PORT || 3003;
FILE: benchmark/frameworks/node-express/server.mjs
constant PORT (line 11) | const PORT = process.env.PORT || 3001;
FILE: benchmark/frameworks/node-fastify/server.mjs
constant PORT (line 14) | const PORT = process.env.PORT || 3002;
FILE: benchmark/frameworks/preact/server.mjs
constant PORT (line 11) | const PORT = process.env.PORT || 3006;
FILE: benchmark/frameworks/render-bench.ts
constant ITERATIONS (line 22) | const ITERATIONS = 100;
function measureTimeUs (line 24) | function measureTimeUs(fn: () => string): [string, number] {
function formatTimeUs (line 31) | function formatTimeUs(us: number): string {
type Result (line 37) | interface Result {
function benchmark (line 44) | function benchmark(name: string, component: React.ComponentType): Result {
FILE: benchmark/frameworks/shared/cx.js
function cx (line 3) | function cx(classes) {
FILE: benchmark/runner/runner.mjs
constant CONFIG (line 23) | const CONFIG = {
constant FRAMEWORKS (line 46) | const FRAMEWORKS = [
constant SCENARIOS (line 56) | const SCENARIOS = [
function checkHealth (line 101) | async function checkHealth(port) {
function waitForServer (line 110) | async function waitForServer(port, maxAttempts = 30) {
function warmup (line 120) | async function warmup(port, scenario, count) {
function parseWrkOutput (line 137) | function parseWrkOutput(output) {
function runWrk (line 195) | async function runWrk(port, scenario, duration, connections, threads) {
function runSimpleBenchmark (line 212) | async function runSimpleBenchmark(port, scenario, requests, concurrency) {
function startFramework (line 268) | function startFramework(framework) {
function stopFramework (line 297) | function stopFramework(framework) {
function cleanup (line 305) | function cleanup() {
function runBenchmark (line 318) | async function runBenchmark(options = {}) {
function generateMarkdown (line 448) | function generateMarkdown(results) {
FILE: demo/client/build.mjs
function build (line 5) | async function build(entryPoints, { env, output, extract, mockWebpackReq...
function parseArgv (line 56) | function parseArgv(argv) {
function parseValue (line 93) | function parseValue(value) {
function camelCaseKeys (line 101) | function camelCaseKeys(obj) {
FILE: packages/Belt/src/stubs.c
function CAMLprim (line 5) | CAMLprim value belt_makemutablelist(value a, value l) {
FILE: packages/esbuild-plugin/plugin.mjs
function writeFile (line 5) | async function writeFile(path, contents, cb) {
function generateBootstrapFile (line 10) | async function generateBootstrapFile(output, content) {
function escapeRegex (line 25) | function escapeRegex(string) {
function plugin (line 29) | function plugin(config) {
FILE: packages/esbuild-plugin/test/ClientComponent.js
function make_client (line 2) | function make_client() { }
FILE: packages/esbuild-plugin/test/ClientComponentWithModule.js
function make_client (line 2) | function make_client() { }
FILE: packages/esbuild-plugin/test/ServerFunction.js
function serverFunction (line 2) | function serverFunction() { }
function serverFunctionWithModule (line 5) | function serverFunctionWithModule() { }
FILE: packages/react-server-dom-esbuild/ReactServerDOMEsbuild.js
method createStringDecoder (line 52) | createStringDecoder() {
method readPartialStringChunk (line 62) | readPartialStringChunk(decoder, buffer) {
method readFinalStringChunk (line 70) | readFinalStringChunk(decoder, buffer) {
method bindToConsole (line 111) | bindToConsole(methodName, args, badgeName) {
constant NAME (line 157) | const NAME = 1;
constant BUNDLES (line 158) | const BUNDLES = 2;
method prepareDestinationForModule (line 181) | prepareDestinationForModule(moduleLoading, nonce, metadata) {
method resolveClientReference (line 198) | resolveClientReference(bundlerConfig, metadata) {
method resolveServerReference (line 219) | resolveServerReference(bundlerConfig, ref) {
method preloadModule (line 239) | preloadModule(metadata) {
method requireModule (line 257) | requireModule(metadata) {
function startReadingFromStream (line 334) | function startReadingFromStream(response, stream) {
function callCurrentServerCallback (line 359) | function callCurrentServerCallback(callServer) {
function createFromReadableStream (line 377) | function createFromReadableStream(stream, options) {
function createResponseFromOptions (line 401) | function createResponseFromOptions(options) {
function createFromFetch (line 429) | function createFromFetch(promise, options) {
Condensed preview — 794 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,316K chars).
[
{
"path": ".dockerignore",
"chars": 154,
"preview": "**/_build\n**/_opam\n**/_opam_*\n_opam*\nnode_modules/\n**/node_modules/\ncompare/\n**/.merlin\n*.install\n.git/\n\nREADME.md\nCHANG"
},
{
"path": ".gitattributes",
"chars": 181,
"preview": "*.re linguist-language=Reason\n*.rei linguist-language=Reason\n\n*.ml linguist-language=OCaml\n*.mli linguist-language=OCaml"
},
{
"path": ".githooks/pre-push",
"chars": 127,
"preview": "#!/bin/bash\n\nif ! ( make format-check ); then\n echo \"some files are not properly formatted, refusing to push\"\n exi"
},
{
"path": ".github/FUNDING.yml",
"chars": 73,
"preview": "# These are supported funding model platforms\n\ngithub: davesnx jchavarri\n"
},
{
"path": ".github/workflows/benchmark.yml",
"chars": 2666,
"preview": "name: Framework Comparison\n\non:\n push:\n tags:\n - 'v*'\n workflow_dispatch:\n inputs:\n frameworks:\n "
},
{
"path": ".github/workflows/ci.yml",
"chars": 3994,
"preview": "name: CI\n\non:\n push:\n branches:\n - main\n tags:\n - '*'\n pull_request:\n branches:\n - main\n\nenv:\n DUNE"
},
{
"path": ".github/workflows/docker.yml",
"chars": 659,
"preview": "name: Docker\n\non:\n push:\n branches:\n - main\n pull_request:\n branches:\n - main\n\nconcurrency:\n group: docke"
},
{
"path": ".gitignore",
"chars": 725,
"preview": "### OS ###\n.DS_Store\n\n### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids"
},
{
"path": ".ocamlformat",
"chars": 42,
"preview": "profile=default\nmargin=120\nversion=0.28.1\n"
},
{
"path": "CHANGES.md",
"chars": 8249,
"preview": "# Changes\n\n## 0.5.0\n\n* Support Promise caching in react.client.components by @davesnx\n* Reorder head content exactly lik"
},
{
"path": "Dockerfile",
"chars": 1770,
"preview": "FROM ocaml/opam:ubuntu-22.04-ocaml-5.4 AS builder\n\nRUN sudo apt-get update && sudo apt-get install -y --no-install-recom"
},
{
"path": "LICENSE.md",
"chars": 1093,
"preview": "Copyright 2024 David Sancho & Javier Chavarri (ml-in-barcelona team)\n\nPermission is hereby granted, free of charge, to a"
},
{
"path": "Makefile",
"chars": 4761,
"preview": "project_name = server-reason-react\n\nDUNE = opam exec -- dune\nopam_file = $(project_name).opam\n\n.PHONY: help\nhelp: ## Pri"
},
{
"path": "README.md",
"chars": 6272,
"preview": "# server-reason-react\n\nNative implementation of React's server-side rendering (SSR) and React Server Components (RSC) ar"
},
{
"path": "arch/browser/.gitignore",
"chars": 310,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
},
{
"path": "arch/browser/package.json",
"chars": 844,
"preview": "{\n \"name\": \"cra\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"dependencies\": {\n \"@testing-library/dom\": \"^10.4.1\",\n "
},
{
"path": "arch/browser/public/index.html",
"chars": 1721,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <link rel=\"icon\" href=\"%PUBLIC_URL%/favicon.i"
},
{
"path": "arch/browser/public/manifest.json",
"chars": 492,
"preview": "{\n \"short_name\": \"React App\",\n \"name\": \"Create React App Sample\",\n \"icons\": [\n {\n \"src\": \"favicon.ico\",\n "
},
{
"path": "arch/browser/src/index.css",
"chars": 366,
"preview": "body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Can"
},
{
"path": "arch/browser/src/index.js",
"chars": 967,
"preview": "import React from 'react';\nimport ReactDOM from 'react-dom/client';\n\nconst App = () => {\n return (\n <html>\n <he"
},
{
"path": "arch/server/head-ordering.js",
"chars": 3370,
"preview": "/**\n * Reference file for React 19.1's head element ordering behavior.\n * Run: node head-ordering.js (from arch/server/)"
},
{
"path": "arch/server/package.json",
"chars": 397,
"preview": "{\n \"name\": \"app\",\n \"version\": \"0.0.1\",\n \"scripts\": {\n \"react-dom-server\": \"bun react-dom-server.js\",\n \"render-h"
},
{
"path": "arch/server/react-dom-server-node-dom-props.js",
"chars": 22807,
"preview": "var properties = {}; // These props are reserved by React. They shouldn't be written to the DOM.\n\nvar reservedProps = [\n"
},
{
"path": "arch/server/react-dom-server.js",
"chars": 2261,
"preview": "const React = require(\"react\");\nconst ReactDOM = require(\"react-dom/server\");\n\nlet first = React.createElement(\n\t\"div\",\n"
},
{
"path": "arch/server/render-html-to-stream.js",
"chars": 2773,
"preview": "import React from \"react\";\nimport * as ReactDOM from \"react-dom/server\";\nimport { prefetchDNS, preconnect, preload, prei"
},
{
"path": "arch/server/render-rsc-to-stream.js",
"chars": 1290,
"preview": "import React from \"react\";\nimport { renderToPipeableStream } from \"react-server-dom-webpack/server\";\nimport { prefetchDN"
},
{
"path": "arch/server/test-useid-edge-cases.js",
"chars": 5796,
"preview": "const React = require(\"react\");\nconst ReactDOM = require(\"react-dom/server\");\n\nfunction DivWithId({ label }) {\n\tconst id"
},
{
"path": "arch/server/test-useid.js",
"chars": 4355,
"preview": "const React = require(\"react\");\nconst ReactDOM = require(\"react-dom/server\");\n\n// Helper: component that renders a div w"
},
{
"path": "benchmark/Makefile",
"chars": 8361,
"preview": "# Server Reason React Benchmark Suite\n# ==================================\n\n.PHONY: all build build-native build-js clea"
},
{
"path": "benchmark/README.md",
"chars": 10608,
"preview": "# Server Reason React Benchmark Suite\n\nA comprehensive benchmark suite for measuring and comparing SSR performance of `s"
},
{
"path": "benchmark/allocation.ml",
"chars": 2809,
"preview": "let measure_alloc title f =\n let before = Gc.stat () in\n let result = f () in\n let after = Gc.stat () in\n Printf.pri"
},
{
"path": "benchmark/bench.ml",
"chars": 8542,
"preview": "(* Benchmark runner with JSON output for github-action-benchmark (customBiggerIsBetter) *)\n\nlet json_mode = ref false\nle"
},
{
"path": "benchmark/dune",
"chars": 602,
"preview": "(executable\n (name bench)\n (modules bench)\n (libraries\n unix\n lwt\n lwt.unix\n benchmark_scenarios\n server-reason-rea"
},
{
"path": "benchmark/frameworks/bun-native/server.tsx",
"chars": 1582,
"preview": "/**\n * Bun Native + React SSR Benchmark Server\n */\n\nimport React from \"react\";\nimport ReactDOMServer from \"react-dom/ser"
},
{
"path": "benchmark/frameworks/hono-bun/server.ts",
"chars": 1384,
"preview": "/**\n * Hono + Bun + React SSR Benchmark Server\n */\n\nimport { Hono } from \"hono\";\nimport React from \"react\";\nimport React"
},
{
"path": "benchmark/frameworks/hono-node/server.mjs",
"chars": 1423,
"preview": "/**\n * Hono + Node.js + React SSR Benchmark Server\n */\n\nimport { serve } from \"@hono/node-server\";\nimport { Hono } from "
},
{
"path": "benchmark/frameworks/node-express/server.mjs",
"chars": 1372,
"preview": "/**\n * Node.js + Express + React SSR Benchmark Server\n */\n\nimport express from \"express\";\nimport React from \"react\";\nimp"
},
{
"path": "benchmark/frameworks/node-fastify/server.mjs",
"chars": 1525,
"preview": "/**\n * Node.js + Fastify + React SSR Benchmark Server\n */\n\nimport Fastify from \"fastify\";\nimport React from \"react\";\nimp"
},
{
"path": "benchmark/frameworks/package.json",
"chars": 1291,
"preview": "{\n \"name\": \"benchmark-frameworks\",\n \"version\": \"1.0.0\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"bui"
},
{
"path": "benchmark/frameworks/preact/server.mjs",
"chars": 7037,
"preview": "/**\n * Node.js + Express + Preact SSR Benchmark Server\n * Tests Preact's lighter-weight alternative to React\n */\n\nimport"
},
{
"path": "benchmark/frameworks/render-bench.ts",
"chars": 3160,
"preview": "/**\n * Pure Render Benchmark - No HTTP\n * Comparable to streaming_bench.ml\n */\n\nif (process.env.NODE_ENV !== \"production"
},
{
"path": "benchmark/frameworks/shared/Blog.jsx",
"chars": 13412,
"preview": "// Port of benchmark/scenarios/Blog.re\n// Purpose: content-heavy page rendering with nested comments\n\nimport React from "
},
{
"path": "benchmark/frameworks/shared/Dashboard.jsx",
"chars": 10391,
"preview": "// Port of benchmark/scenarios/Dashboard.re\n// Purpose: admin/dashboard UI rendering\n\nimport React from \"react\";\nimport "
},
{
"path": "benchmark/frameworks/shared/Ecommerce.jsx",
"chars": 13664,
"preview": "// Port of benchmark/scenarios/Ecommerce.re\n// Purpose: real-world SSR performance for e-commerce page\n\nimport React fro"
},
{
"path": "benchmark/frameworks/shared/Form.jsx",
"chars": 15224,
"preview": "// Port of benchmark/scenarios/Form.re\n// Purpose: form-heavy page rendering\n\nimport React from \"react\";\nimport { cx } f"
},
{
"path": "benchmark/frameworks/shared/PropsHeavy.jsx",
"chars": 6306,
"preview": "// Port of benchmark/scenarios/PropsHeavy.re\n// Purpose: test attribute serialization performance\n\nimport React from \"re"
},
{
"path": "benchmark/frameworks/shared/cx.js",
"chars": 231,
"preview": "// Matches benchmark/scenarios/Cx.re exactly:\n// let make = cns => cns |> List.filter(x => x != \"\") |> String.concat(\""
},
{
"path": "benchmark/frameworks/shared/scenarios.jsx",
"chars": 14602,
"preview": "/**\n * Shared benchmark scenarios for JavaScript frameworks\n * These mirror the Reason/OCaml scenarios for fair comparis"
},
{
"path": "benchmark/memory/dune",
"chars": 199,
"preview": "(executable\n (name memory_bench)\n (libraries\n server-reason-react.js\n server-reason-react.react\n server-reason-react."
},
{
"path": "benchmark/memory/memory_bench.ml",
"chars": 6521,
"preview": "(** Memory Benchmark Suite for server-reason-react\n\n Measures:\n - Memory allocation per render\n - GC pressure ("
},
{
"path": "benchmark/native/dune",
"chars": 233,
"preview": "(executable\n (name server)\n (libraries\n unix\n dream\n lwt\n lwt.unix\n server-reason-react.js\n server-reason-react.re"
},
{
"path": "benchmark/native/server.re",
"chars": 5259,
"preview": "/* Dream + server-reason-react Benchmark Server */\nopen Benchmark_scenarios;\n\nlet render_scenario = scenario_name => {\n "
},
{
"path": "benchmark/perf-work/PERF_NEXT.md",
"chars": 13583,
"preview": "# SSR Perf — Next Phase Plan\n\nStatus: research-only document. No code in `packages/` changes from this plan\nuntil hypoth"
},
{
"path": "benchmark/perf-work/README.md",
"chars": 10458,
"preview": "# SSR Perf Work — Results\n\nTracking SSR optimization work toward 5x vs Bun.\n\n## TL;DR\n\n| Metric | Phase 0 (baseline) | *"
},
{
"path": "benchmark/perf-work/alloc-table500.txt",
"chars": 15177,
"preview": "=== alloc_profile ===\nsampling_rate=0.0001 iterations=2000 top=20\n\n### table500\n iterations: 2000 elapsed: 3.376s p"
},
{
"path": "benchmark/perf-work/alloc-wide500.txt",
"chars": 14802,
"preview": "=== alloc_profile ===\nsampling_rate=0.0001 iterations=2000 top=20\n\n### wide500\n iterations: 2000 elapsed: 2.774s pe"
},
{
"path": "benchmark/perf-work/alloc_profile.ml",
"chars": 7550,
"preview": "(* Allocation profiler for SSR rendering.\n\n Uses [Gc.Memprof] to attribute minor/major-heap allocations to source\n l"
},
{
"path": "benchmark/perf-work/baseline-run1.txt",
"chars": 1211,
"preview": "Trivial 0.33µs 0.41µs 22B 66.4MB/s\nShallowTree 39.35µs 44.47µs "
},
{
"path": "benchmark/perf-work/baseline-run2.txt",
"chars": 1211,
"preview": "Trivial 0.27µs 0.23µs 22B 82.4MB/s\nShallowTree 39.14µs 43.69µs "
},
{
"path": "benchmark/perf-work/baseline-run3.txt",
"chars": 1211,
"preview": "Trivial 0.33µs 0.28µs 22B 66.9MB/s\nShallowTree 36.70µs 42.14µs "
},
{
"path": "benchmark/perf-work/cycles-out/callgrind-table100.annotate.stderr",
"chars": 0,
"preview": ""
},
{
"path": "benchmark/perf-work/cycles-out/callgrind-table100.out",
"chars": 262729,
"preview": "# callgrind format\nversion: 1\ncreator: callgrind-3.19.0\npid: 2491719\ncmd: /home/me/server-reason-react/_build/default/b"
},
{
"path": "benchmark/perf-work/cycles-out/callgrind-table100.stderr",
"chars": 570,
"preview": "==2491719== Callgrind, a call-graph generating cache profiler\n==2491719== Copyright (C) 2002-2017, and GNU GPL'd, by Jos"
},
{
"path": "benchmark/perf-work/cycles-out/callgrind-table100.stdout",
"chars": 152,
"preview": "=== perf_profile ===\nwarmup=5 iters=30 scenarios=1\n### table100\n iterations: 30 elapsed: 0.696s per-iter: 23204.20µ"
},
{
"path": "benchmark/perf-work/cycles-out/callgrind-table100.txt",
"chars": 41527,
"preview": "--------------------------------------------------------------------------------\nProfile data file 'benchmark/perf-work/"
},
{
"path": "benchmark/perf-work/cycles-out/callgrind-wide100.annotate.stderr",
"chars": 0,
"preview": ""
},
{
"path": "benchmark/perf-work/cycles-out/callgrind-wide100.out",
"chars": 237102,
"preview": "# callgrind format\nversion: 1\ncreator: callgrind-3.19.0\npid: 2491614\ncmd: /home/me/server-reason-react/_build/default/b"
},
{
"path": "benchmark/perf-work/cycles-out/callgrind-wide100.stderr",
"chars": 569,
"preview": "==2491614== Callgrind, a call-graph generating cache profiler\n==2491614== Copyright (C) 2002-2017, and GNU GPL'd, by Jos"
},
{
"path": "benchmark/perf-work/cycles-out/callgrind-wide100.stdout",
"chars": 150,
"preview": "=== perf_profile ===\nwarmup=5 iters=30 scenarios=1\n### wide100\n iterations: 30 elapsed: 0.505s per-iter: 16823.17µs"
},
{
"path": "benchmark/perf-work/cycles-out/callgrind-wide100.txt",
"chars": 36149,
"preview": "--------------------------------------------------------------------------------\nProfile data file '/home/me/server-reas"
},
{
"path": "benchmark/perf-work/cycles-out/perf-stat-wide100.stdout",
"chars": 453,
"preview": "=== perf_profile ===\nwarmup=50 iters=500 scenarios=1\n### wide100\n iterations: 500 elapsed: 0.113s per-iter: 225.89µ"
},
{
"path": "benchmark/perf-work/cycles-out/perf-stat-wide100.txt",
"chars": 662,
"preview": "\n Performance counter stats for '/home/me/server-reason-react/_build/default/benchmark/perf-work/perf_profile.exe --scen"
},
{
"path": "benchmark/perf-work/diff_styles.ml",
"chars": 591,
"preview": "(* Compare output of a known style, expected vs actual *)\nlet () =\n let s =\n ReactDOM.Style.make ~backgroundColor:\"#"
},
{
"path": "benchmark/perf-work/dump_html.ml",
"chars": 521,
"preview": "(* Dump HTML for comparison *)\nlet () =\n let which = try Sys.argv.(1) with _ -> \"PropsSmall\" in\n let html =\n match "
},
{
"path": "benchmark/perf-work/dune",
"chars": 267,
"preview": "(executables\n (names\n style_alloc_bench\n diff_styles\n dump_html\n alloc_profile\n perf_profile\n unification_bench)\n "
},
{
"path": "benchmark/perf-work/dune_extra",
"chars": 119,
"preview": "(executable\n (name diff_styles)\n (libraries server-reason-react.reactDom)\n (preprocess (pps server-reason-react.ppx)))\n"
},
{
"path": "benchmark/perf-work/perf_profile.ml",
"chars": 8325,
"preview": "(* CPU-cycle profiling runner for SSR rendering.\n\n Companion to [alloc_profile.ml]. Where the allocation profiler answ"
},
{
"path": "benchmark/perf-work/perf_profile.sh",
"chars": 10104,
"preview": "#!/usr/bin/env bash\n# CPU-cycle profiler driver for SSR rendering.\n#\n# Orchestrates external profilers around perf_profi"
},
{
"path": "benchmark/perf-work/phase1-run1.txt",
"chars": 1211,
"preview": "Trivial 0.33µs 0.31µs 22B 67.4MB/s\nShallowTree 37.84µs 42.77µs "
},
{
"path": "benchmark/perf-work/phase1-run2.txt",
"chars": 1211,
"preview": "Trivial 0.30µs 0.27µs 22B 73.2MB/s\nShallowTree 39.20µs 43.39µs "
},
{
"path": "benchmark/perf-work/phase2-final.txt",
"chars": 1211,
"preview": "Trivial 0.31µs 0.28µs 22B 70.4MB/s\nShallowTree 38.51µs 43.98µs "
},
{
"path": "benchmark/perf-work/phase2-run1.txt",
"chars": 1211,
"preview": "Trivial 0.27µs 0.18µs 22B 80.9MB/s\nShallowTree 37.40µs 43.21µs "
},
{
"path": "benchmark/perf-work/phase2-run2.txt",
"chars": 1211,
"preview": "Trivial 0.26µs 0.24µs 22B 83.9MB/s\nShallowTree 37.03µs 42.31µs "
},
{
"path": "benchmark/perf-work/phase3-run1.txt",
"chars": 1211,
"preview": "Trivial 0.33µs 0.35µs 22B 66.4MB/s\nShallowTree 46.42µs 35.36µs "
},
{
"path": "benchmark/perf-work/phase3-run2.txt",
"chars": 1211,
"preview": "Trivial 0.33µs 0.30µs 22B 65.9MB/s\nShallowTree 55.10µs 38.84µs "
},
{
"path": "benchmark/perf-work/phase3b-run1.txt",
"chars": 1211,
"preview": "Trivial 0.36µs 0.28µs 22B 60.7MB/s\nShallowTree 37.46µs 38.01µs "
},
{
"path": "benchmark/perf-work/phase3b-run2.txt",
"chars": 1211,
"preview": "Trivial 0.33µs 0.27µs 22B 66.4MB/s\nShallowTree 38.03µs 38.11µs "
},
{
"path": "benchmark/perf-work/phase3c-run1.txt",
"chars": 1211,
"preview": "Trivial 0.33µs 0.24µs 22B 66.9MB/s\nShallowTree 38.16µs 35.39µs "
},
{
"path": "benchmark/perf-work/phase3c-run2.txt",
"chars": 1211,
"preview": "Trivial 0.39µs 0.31µs 22B 56.3MB/s\nShallowTree 48.54µs 39.60µs "
},
{
"path": "benchmark/perf-work/phase4-final.txt",
"chars": 1211,
"preview": "Trivial 0.33µs 0.30µs 22B 66.9MB/s\nShallowTree 36.89µs 35.09µs "
},
{
"path": "benchmark/perf-work/phase4-run1.txt",
"chars": 3645,
"preview": "Trivial 0.32µs 0.28µs 22B 67.8MB/s\nShallowTree 35.94µs 34.27µs "
},
{
"path": "benchmark/perf-work/phase7-final.txt",
"chars": 1211,
"preview": "Trivial 0.34µs 0.31µs 22B 65.4MB/s\nShallowTree 36.05µs 33.41µs "
},
{
"path": "benchmark/perf-work/style_alloc_bench.ml",
"chars": 824,
"preview": "(* Measure Style.make allocation after PPX rewrite. *)\n\nlet bench label f =\n Gc.full_major ();\n Gc.compact ();\n let b"
},
{
"path": "benchmark/perf-work/unification_bench.ml",
"chars": 2054,
"preview": "(* Microbench: Static vs Writer unification. *)\n\nlet bench label iterations f =\n Gc.full_major ();\n Gc.compact ();\n l"
},
{
"path": "benchmark/perf-work/unified-experiment-runs.txt",
"chars": 3645,
"preview": "Trivial 0.31µs 0.24µs 22B 72.1MB/s\nShallowTree 34.43µs 35.67µs "
},
{
"path": "benchmark/perf-work/unified-experiment.txt",
"chars": 1211,
"preview": "Trivial 0.39µs 0.28µs 22B 56.3MB/s\nShallowTree 35.48µs 37.19µs "
},
{
"path": "benchmark/results/.gitkeep",
"chars": 78,
"preview": "# Benchmark results directory\n# JSON and markdown reports are generated here\n\n"
},
{
"path": "benchmark/runner/package.json",
"chars": 398,
"preview": "{\n \"name\": \"benchmark-runner\",\n \"version\": \"1.0.0\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"bench\":"
},
{
"path": "benchmark/runner/runner.mjs",
"chars": 17045,
"preview": "#!/usr/bin/env node\n\n/**\n * Comprehensive Benchmark Runner\n *\n * Features:\n * - Multi-framework comparison\n * - Multiple"
},
{
"path": "benchmark/runner/visualize.html",
"chars": 15451,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, in"
},
{
"path": "benchmark/scenarios/Blog.re",
"chars": 18053,
"preview": "/* Scenario: Blog Article Page\n Article with comments, sidebar, rich content\n Purpose: Test content-heavy page"
},
{
"path": "benchmark/scenarios/Cx.re",
"chars": 128,
"preview": "/* Simple classname utility for benchmarks */\nlet make = cns => cns |> String.concat(\" \");\nlet ifTrue = (cn, x) => x ? c"
},
{
"path": "benchmark/scenarios/Dashboard.re",
"chars": 13758,
"preview": "/* Scenario: Analytics Dashboard\n Complex dashboard with stats, charts placeholders, and data tables\n Purpose:"
},
{
"path": "benchmark/scenarios/DeepTree.re",
"chars": 1438,
"preview": "/* Scenario: Deep Tree\n 50+ levels deep component tree\n Purpose: Test deep recursion and call stack performanc"
},
{
"path": "benchmark/scenarios/Ecommerce.re",
"chars": 19862,
"preview": "/* Scenario: E-commerce Page\n Realistic product listing page with complex UI patterns\n Purpose: Test real-worl"
},
{
"path": "benchmark/scenarios/Form.re",
"chars": 19052,
"preview": "/* Scenario: Complex Form\n Multi-step form with many inputs and validation UI\n Purpose: Test form-heavy page r"
},
{
"path": "benchmark/scenarios/PropsHeavy.re",
"chars": 7646,
"preview": "/* Scenario: Props Heavy\n Components with many HTML attributes/props\n Purpose: Test attribute serialization pe"
},
{
"path": "benchmark/scenarios/ShallowTree.re",
"chars": 2603,
"preview": "/* Scenario: Shallow Tree\n 5 components deep with multiple props each\n Purpose: Test prop passing and shallow "
},
{
"path": "benchmark/scenarios/Table.re",
"chars": 8877,
"preview": "/* Scenario: Table Rendering\n Real-world data table patterns\n Purpose: Test realistic table rendering performa"
},
{
"path": "benchmark/scenarios/Trivial.re",
"chars": 218,
"preview": "/* Scenario: Trivial\n Baseline test - simplest possible component\n Purpose: Measure baseline overhead of React"
},
{
"path": "benchmark/scenarios/WideTree.re",
"chars": 4222,
"preview": "/* Scenario: Wide Tree\n Many siblings at the same level (tests list/array rendering)\n Purpose: Test horizontal"
},
{
"path": "benchmark/scenarios/dune",
"chars": 213,
"preview": "(library\n (name benchmark_scenarios)\n (libraries\n server-reason-react.react\n server-reason-react.reactDom\n server-rea"
},
{
"path": "benchmark/streaming/dune",
"chars": 234,
"preview": "(executable\n (name streaming_bench)\n (libraries\n unix\n lwt\n lwt.unix\n server-reason-react.js\n server-reason-react.r"
},
{
"path": "benchmark/streaming/streaming_bench.ml",
"chars": 4940,
"preview": "(** Streaming Benchmark for server-reason-react\n\n Measures:\n - Time to first byte (TTFB)\n - Time to full render"
},
{
"path": "demo/README.md",
"chars": 1881,
"preview": "## Requirements\n\n- npm (and run `npm install` from the root of the project)\n- [watchexec](https://github.com/watchexec/w"
},
{
"path": "demo/client/DummyRouterRSC.re",
"chars": 3624,
"preview": "module DOM = Webapi.Dom;\nmodule Location = DOM.Location;\nmodule History = DOM.History;\nmodule ReadableStream = Webapi.Re"
},
{
"path": "demo/client/HydrateRoot.re",
"chars": 216,
"preview": "let element = Webapi.Dom.Document.querySelector(\"#root\", Webapi.Dom.document);\n\nswitch (element) {\n| Some(el) =>\n let _"
},
{
"path": "demo/client/NestedRouterRSC.re",
"chars": 1280,
"preview": "module ReadableStream = Webapi.ReadableStream;\n\nexternal readable_stream: ReadableStream.t =\n \"window.srr_stream.readab"
},
{
"path": "demo/client/RenderRoot.re",
"chars": 256,
"preview": "module Dom = Webapi.Dom;\n\nlet element = Dom.Document.querySelector(\"#root\", Dom.document);\n\nswitch (element) {\n| Some(el"
},
{
"path": "demo/client/ServerOnlyRSC.re",
"chars": 516,
"preview": "let root =\n Webapi.Dom.document\n |> Webapi.Dom.Document.querySelector(\"#root\")\n |> Option.get;\n\nlet root = ReactDOM.C"
},
{
"path": "demo/client/SinglePageRSC.re",
"chars": 1123,
"preview": "let callServer = (path: string, args) => {\n let headers =\n Fetch.HeadersInit.make({\n \"Accept\": \"application/rea"
},
{
"path": "demo/client/build.mjs",
"chars": 2801,
"preview": "import Esbuild from \"esbuild\";\nimport Path from \"path\";\nimport extractClientComponents from \"../../packages/esbuild-plug"
},
{
"path": "demo/client/dune",
"chars": 1058,
"preview": "(env\n (_\n (env-vars\n (\"DEMO_ENV\" \"development\"))))\n\n(melange.emit\n (enabled_if\n (= %{profile} dev))\n (target app)\n ("
},
{
"path": "demo/client/package.json",
"chars": 290,
"preview": "{\n \"name\": \"client\",\n \"version\": \"0.0.0\",\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"dependencies\": {\n "
},
{
"path": "demo/dream-nested-router/dune",
"chars": 208,
"preview": "(test\n (name test_router_rsc)\n (enabled_if\n (= %{profile} dev))\n (libraries\n alcotest\n dream\n react\n js\n server-re"
},
{
"path": "demo/dream-nested-router/js/HistoryCache.re",
"chars": 1014,
"preview": "/**\n * HistoryCache is a module that caches the pages.\n * It's used to avoid fetching the same page again when navigatin"
},
{
"path": "demo/dream-nested-router/js/HistoryState.re",
"chars": 1180,
"preview": "module DOM = Webapi.Dom;\nmodule History = DOM.History;\n\n/**\n * Melange webapi don't set state type, so we use Obj.magic "
},
{
"path": "demo/dream-nested-router/js/VirtualHistory.re",
"chars": 1576,
"preview": "/**\n Virtual History is a state of routes that the client has visited.\n It's used to store the routes path and renderP"
},
{
"path": "demo/dream-nested-router/js/dune",
"chars": 564,
"preview": "(library\n (name nested_router_js)\n (enabled_if\n (= %{profile} dev))\n (modes melange)\n (wrapped false)\n (libraries\n rea"
},
{
"path": "demo/dream-nested-router/native/README.md",
"chars": 12227,
"preview": "# Nested Router\n\nThe Nested Router allows us to navigate through the application while requesting only the minimum requi"
},
{
"path": "demo/dream-nested-router/native/RouterRSC.re",
"chars": 20506,
"preview": "/**\n* RouterRSC is a module that provides the helpers to build the route and the layout component from the route definit"
},
{
"path": "demo/dream-nested-router/native/RouterRSC.rei",
"chars": 1902,
"preview": "module type MAIN_LAYOUT = {\n [@react.component]\n let make: (~children: React.element, unit) => React.element;\n};\n\nmodu"
},
{
"path": "demo/dream-nested-router/native/dune",
"chars": 618,
"preview": "(include_subdirs unqualified)\n\n(library\n (name nested_router_native)\n (enabled_if\n (= %{profile} dev))\n (wrapped false)"
},
{
"path": "demo/dream-nested-router/native/shared/DynamicParams.re",
"chars": 333,
"preview": "[@deriving rsc]\ntype t = array((string, string));\n\nlet create = () => [||];\n\nlet add = (t, key, value) => {\n Array.appe"
},
{
"path": "demo/dream-nested-router/native/shared/NavigationResponse.re",
"chars": 778,
"preview": "type navigationCallback =\n (\n ~parentRoute: string,\n ~dynamicParams: DynamicParams.t,\n ~element: React.element"
},
{
"path": "demo/dream-nested-router/native/shared/Route.re",
"chars": 3232,
"preview": "/**\n* Route is the component that renders the route and provides the renderPage function to update page/subroutes when t"
},
{
"path": "demo/dream-nested-router/native/shared/Router.re",
"chars": 11947,
"preview": "/**\n* Router is a component that provides the router context to the application.\n* It provides the dynamic params, url a"
},
{
"path": "demo/dream-nested-router/native/shared/Router.rei",
"chars": 562,
"preview": "exception No_provider(string);\n\ntype url = URL.t;\nlet url_to_rsc: url => RSC.t;\nlet url_of_rsc: RSC.t => url;\n\ntype t =\n"
},
{
"path": "demo/dream-nested-router/test_router_rsc.ml",
"chars": 3495,
"preview": "let test title fn = Alcotest.test_case title `Quick fn\nlet assert_bool left right = Alcotest.check Alcotest.bool \"should"
},
{
"path": "demo/dream-rsc/DreamRSC.re",
"chars": 10119,
"preview": "module RequestContext = {\n type pending_cookie = {\n name: string,\n value: string,\n expires: option(float),\n "
},
{
"path": "demo/dream-rsc/DreamRSC.rei",
"chars": 3348,
"preview": "/** Dream integration for React Server Components.\n\n Provides request context (cookies, headers) accessible from serv"
},
{
"path": "demo/dream-rsc/dune",
"chars": 125,
"preview": "(library\n (name dream_rsc)\n (wrapped false)\n (libraries dream react reactDOM js lwt lwt.unix)\n (preprocess\n (pps lwt_pp"
},
{
"path": "demo/dune",
"chars": 860,
"preview": "(rule\n (alias demo)\n (enabled_if\n (= %{profile} \"dev\"))\n (deps\n server/server.exe\n (alias_rec client))\n (action\n (pr"
},
{
"path": "demo/package.json",
"chars": 171,
"preview": "{\n \"name\": \"server-reason-react-demo\",\n \"version\": \"0.0.0\",\n \"description\": \"\",\n \"dependencies\": {\n \"@tailwindcss"
},
{
"path": "demo/server/db/notes.json",
"chars": 8534,
"preview": "[\n {\n \"id\": 0,\n \"title\": \"Lorem ipsum for markdown, exists\",\n \"content\": \"# Aethere conterminus nec est damno\\"
},
{
"path": "demo/server/dune",
"chars": 408,
"preview": "(include_subdirs qualified)\n\n(executable\n (enabled_if\n (= %{profile} \"dev\"))\n (name server)\n (libraries\n dream\n dream"
},
{
"path": "demo/server/pages/Comments.re",
"chars": 3148,
"preview": "module Post = {\n [@react.component]\n let make = () => {\n <section>\n <p>\n {React.string(\n \"Not"
},
{
"path": "demo/server/pages/DummyRouterRSC.re",
"chars": 5204,
"preview": "let markdownStyles = (~background, ~text) => {\n Printf.sprintf(\n {|\n.markdown h1 {\n font-size: 2.25rem;\n font-weig"
},
{
"path": "demo/server/pages/Home.re",
"chars": 1326,
"preview": "let handler = _request => {\n let app =\n <Document>\n <div className={Cx.make([\"py-16\", \"px-12\"])}>\n <div "
},
{
"path": "demo/server/pages/NestedRouter.re",
"chars": 6369,
"preview": "let markdownStyles = (~background, ~text) => {\n Printf.sprintf(\n {|\n.markdown h1 {\n font-size: 2.25rem;\n font-weig"
},
{
"path": "demo/server/pages/NoteItem.re",
"chars": 2157,
"preview": "open Lwt.Syntax;\n\nmodule NoteView = {\n [@react.component]\n let make = (~note: Note.t) => {\n <div className=\"h-full\""
},
{
"path": "demo/server/pages/NoteList.re",
"chars": 1322,
"preview": "open Lwt.Syntax;\n\nlet is_substring = (a, b) => {\n let len_a = String.length(a);\n let len_b = String.length(b);\n if (l"
},
{
"path": "demo/server/pages/ServerOnlyRSC.re",
"chars": 958,
"preview": "let handler = request => {\n let app =\n <DemoLayout background=Theme.Color.Gray2>\n <div className=\"flex flex-col"
},
{
"path": "demo/server/pages/SidebarNote.re",
"chars": 896,
"preview": "[@react.component]\nlet make = (~note: Note.t) => {\n let lastUpdatedAt =\n if (Date.is_today(note.updated_at)) {\n "
},
{
"path": "demo/server/pages/SinglePageRSC.re",
"chars": 9073,
"preview": "module Section = {\n [@react.component]\n let make = (~title, ~children, ~description=?) => {\n <Stack gap=2 justify=`"
},
{
"path": "demo/server/server.re",
"chars": 2505,
"preview": "let debug = Sys.getenv_opt(\"DEMO_ENV\") == Some(\"development\");\n\n// Allow GET and POST from the same handler enables prog"
},
{
"path": "demo/styles.css",
"chars": 1217,
"preview": "@import \"tailwindcss\";\n\n/* Since we use dynamic classNames on Theme.re, we need to inline the colors here. This can caus"
},
{
"path": "demo/tailwind.config.js",
"chars": 162,
"preview": "/** @type {import('tailwindcss').Config} */\nexport default {\n content: {\n files: [\"./client/*.re\",\"./server/*.re\", \""
},
{
"path": "demo/universal/js/Dream.re",
"chars": 19,
"preview": "let log = Js.log2;\n"
},
{
"path": "demo/universal/js/dune",
"chars": 671,
"preview": "(include_subdirs unqualified)\n\n(library\n (name demo_shared_js)\n (modes melange)\n (wrapped false)\n (libraries\n reason-re"
},
{
"path": "demo/universal/native/DB.re",
"chars": 7037,
"preview": "open Lwt.Syntax;\n\nlet repoRoot = () => {\n let exeDir = Filename.dirname(Sys.executable_name);\n let rec findRoot = dir "
},
{
"path": "demo/universal/native/Date.re",
"chars": 636,
"preview": "let is_today = date => {\n let now = Unix.localtime(Unix.time());\n let d = Unix.localtime(date);\n now.tm_year == d.tm_"
},
{
"path": "demo/universal/native/FunctionReferences.re",
"chars": 172,
"preview": "type t = Hashtbl.t(string, ReactServerDOM.server_function);\n\nlet registry = Hashtbl.create(10);\nlet register = Hashtbl.a"
},
{
"path": "demo/universal/native/FunctionReferences.rei",
"chars": 43,
"preview": "include ReactServerDOM.FunctionReferences;\n"
},
{
"path": "demo/universal/native/Markdown.re",
"chars": 6957,
"preview": "module List = {\n include List;\n let take = (lst, n) => {\n let rec aux = (lst, n, acc) =>\n switch (lst, n) {\n "
},
{
"path": "demo/universal/native/SidebarNote.re",
"chars": 915,
"preview": "[@platform native]\n[@react.component]\nlet make = (~note: Note.t) => {\n let lastUpdatedAt =\n if (Date.is_today(note.u"
},
{
"path": "demo/universal/native/dune",
"chars": 610,
"preview": "(include_subdirs unqualified)\n\n(library\n (name demo_shared_native)\n (flags :standard -w -26-27) ; browser_only removes c"
},
{
"path": "demo/universal/native/shared/Align.re",
"chars": 592,
"preview": "type verticalAlign = [\n | `top\n | `center\n | `bottom\n];\ntype horizontalAlign = [\n | `left\n | `center\n | `right\n];\n"
},
{
"path": "demo/universal/native/shared/App.re",
"chars": 2105,
"preview": "module Hr = {\n [@react.component]\n let make = () => {\n <span\n className={Cx.make([\n \"block\",\n \"w"
},
{
"path": "demo/universal/native/shared/Arrow.re",
"chars": 546,
"preview": "type direction =\n | Left\n | Right;\n\n[@react.component]\nlet make = (~direction: direction=Right) => {\n <svg\n classN"
},
{
"path": "demo/universal/native/shared/Button.re",
"chars": 510,
"preview": "[@warning \"-33\"]\n[@react.client.component]\nlet make = (~noteId: option(int), ~children: React.element) => {\n let (isPen"
},
{
"path": "demo/universal/native/shared/Context.re",
"chars": 135,
"preview": "module Provider = {\n include React.Context;\n let context = React.createContext(23);\n let make = React.Context.provide"
},
{
"path": "demo/universal/native/shared/Counter.re",
"chars": 1319,
"preview": "[@react.client.component]\nlet make = (~initial: int) => {\n let (state, [@browser_only] setCount) = RR.useStateValue(ini"
},
{
"path": "demo/universal/native/shared/Cx.re",
"chars": 344,
"preview": "let make = cns =>\n cns->Belt.List.keep(x => x !== \"\") |> String.concat(\" \") |> String.trim;\n\nlet ifTrue = (cn, x) => x "
},
{
"path": "demo/universal/native/shared/Debug_props.re",
"chars": 2087,
"preview": "[@warning \"-33\"];\n\n[@react.client.component]\nlet make =\n (\n ~string: string,\n ~int: int=999999,\n ~floa"
},
{
"path": "demo/universal/native/shared/DeleteNoteButton.re",
"chars": 816,
"preview": "[@warning \"-26-27-32\"];\n[@react.client.component]\nlet make = (~noteId: int) => {\n let (isNavigating, startNavigating) ="
},
{
"path": "demo/universal/native/shared/DemoLayout.re",
"chars": 1230,
"preview": "type mode =\n | FullScreen\n | Fit;\n\n[@react.component]\nlet make = (~children, ~background=Theme.Color.Gray2, ~mode=Fit)"
},
{
"path": "demo/universal/native/shared/Document.re",
"chars": 709,
"preview": "[@react.component]\nlet make = (~children, ~script=?, ~suppressHydrationWarning=true) => {\n <html suppressHydrationWarni"
},
{
"path": "demo/universal/native/shared/DummyClientRouter.re",
"chars": 1994,
"preview": "type location = {\n selectedId: option(int),\n isEditing: bool,\n searchText: option(string),\n};\n\nlet locationToString ="
},
{
"path": "demo/universal/native/shared/Expander.re",
"chars": 1346,
"preview": "[@warning \"-26-27-32\"];\n\n[@react.client.component]\nlet make =\n (\n ~id: int,\n ~title: string,\n ~childre"
},
{
"path": "demo/universal/native/shared/GlobalStyles.re",
"chars": 642,
"preview": "let string =\n Printf.sprintf(\n {js|\n html, body, #root {\n margin: 0;\n padding: 0;\n width: 100vw;\n heigh"
},
{
"path": "demo/universal/native/shared/Hr.re",
"chars": 202,
"preview": "[@react.component]\nlet make = () => {\n <hr\n className={Cx.make([\n \"block\",\n \"w-full\",\n \"h-[1px]\",\n "
},
{
"path": "demo/universal/native/shared/InputText.re",
"chars": 313,
"preview": "[@react.component]\nlet make = (~value, ~onChange, ~id=\"\", ~placeholder=\"\") =>\n <input\n className={Cx.make([\n \"m"
},
{
"path": "demo/universal/native/shared/Link.re",
"chars": 1129,
"preview": "let defaultSize = Text.Medium;\n\nmodule Base = {\n [@react.component]\n let make = (~size, ~color, ~href, ~children, ~und"
},
{
"path": "demo/universal/native/shared/NestedRouter_CreateNoteButton.re",
"chars": 346,
"preview": "[@react.client.component]\nlet make = (~children: React.element) => {\n let navigate = Router.use();\n let (isPending, st"
},
{
"path": "demo/universal/native/shared/NestedRouter_DeleteNoteButton.re",
"chars": 1226,
"preview": "module DOM = Webapi.Dom;\nmodule Location = DOM.Location;\nmodule Window = DOM.Window;\n\n[@react.client.component]\nlet make"
},
{
"path": "demo/universal/native/shared/NestedRouter_EditButton.re",
"chars": 421,
"preview": "[@react.client.component]\nlet make = (~noteId: int, ~children: React.element) => {\n let (isPending, startTransition) = "
},
{
"path": "demo/universal/native/shared/NestedRouter_NoteEditor.re",
"chars": 2222,
"preview": "[@react.client.component]\nlet make =\n (~noteId: option(int), ~initialTitle: string, ~initialBody: string) => {\n let "
},
{
"path": "demo/universal/native/shared/NestedRouter_NoteItem.re",
"chars": 2391,
"preview": "[@platform native]\nmodule NoteView = {\n [@react.component]\n let make = (~note: Note.t) => {\n <div className=\"h-full"
},
{
"path": "demo/universal/native/shared/NestedRouter_NoteList.re",
"chars": 2540,
"preview": "let is_substring = (a, b) => {\n let len_a = String.length(a);\n let len_b = String.length(b);\n if (len_a > len_b) {\n "
},
{
"path": "demo/universal/native/shared/NestedRouter_SearchField.re",
"chars": 1376,
"preview": "[@react.client.component]\nlet make = () => {\n let { Router.navigate, url, _ } = Router.useRouter();\n let queryParams ="
},
{
"path": "demo/universal/native/shared/NestedRouter_SidebarNote.re",
"chars": 770,
"preview": "[@deriving rsc]\ntype notePreview = {\n id: int,\n title: string,\n content: string,\n updated_at: string,\n};\n\n[@react.cl"
},
{
"path": "demo/universal/native/shared/NestedRouter_SidebarNoteContent.re",
"chars": 2175,
"preview": "module DOM = Webapi.Dom;\nmodule Location = DOM.Location;\n\nmodule Square = {\n [@react.component]\n let make = (~isExpand"
},
{
"path": "demo/universal/native/shared/Note.re",
"chars": 282,
"preview": "[@deriving rsc]\ntype t = {\n id: int,\n title: string,\n content: string,\n updated_at: float,\n};\n\nlet pp = note => {\n "
},
{
"path": "demo/universal/native/shared/NoteEditor.re",
"chars": 2124,
"preview": "[@warning \"-26-27-32\"];\n\n[@react.client.component]\nlet make =\n (~noteId: option(int), ~initialTitle: string, ~initial"
},
{
"path": "demo/universal/native/shared/NoteListSkeleton.re",
"chars": 1057,
"preview": "[@react.component]\nlet make = () => {\n <div className=\"mt-8\">\n <ul className=\"flex flex-col\">\n <li className=\"v"
},
{
"path": "demo/universal/native/shared/NotePreview.re",
"chars": 284,
"preview": "[@react.component]\nlet make = (~body: string) => {\n <span\n className={Cx.make([\n \"markdown\",\n \"block w-ful"
},
{
"path": "demo/universal/native/shared/NoteSkeleton.re",
"chars": 158,
"preview": "[@react.component]\nlet make = (~isEditing as _) => {\n <div className=\"flex items-center justify-center h-full\">\n <Te"
},
{
"path": "demo/universal/native/shared/Promise_renderer.re",
"chars": 702,
"preview": "[@warning \"-33\"];\n\nmodule Reader = {\n [@react.component]\n let make = (~promise: Js.Promise.t(string)) => {\n let val"
},
{
"path": "demo/universal/native/shared/RR.re",
"chars": 628,
"preview": "[@platform native]\ninclude {\n let useStateValue = initialState => {\n let setValueStatic = _newState "
}
]
// ... and 594 more files (download for full content)
About this extraction
This page contains the full source code of the ml-in-barcelona/server-reason-react GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 794 files (3.9 MB), approximately 1.1M tokens, and a symbol index with 91 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.