Automatically wrap JSX string literals with Jsx.string()
This PR implements automatic wrapping of string literals in JSX children with Jsx.string() calls, eliminating the need for manual wrapping.
Problem
Currently, string literals in JSX children must be explicitly wrapped:
// Current - requires manual wrapping
<span>{Jsx.string("hello")}</span>
<> {Jsx.string("world")} </>
// What developers want to write
<span>hello</span>
<> world </>
Solution
Modified the JSX children parser in compiler/syntax/src/res_core.ml to automatically detect string literals and wrap them with Jsx.string() calls during AST construction.
Implementation Details
-
Added
wrap_jsx_string_literalfunction that:- Detects string literal expressions (
Pexp_constant (Pconst_string ...)) - Creates AST nodes representing
Jsx.string(originalString)function calls - Preserves original source location information
- Detects string literal expressions (
-
Integrated into
parse_jsx_children:- Applied to both JSX parsing paths (element and fragment children)
- Only affects direct string literals, not expressions in braces
- Maintains backward compatibility with existing code
Examples
After this change, the following transformations happen automatically:
// Input
<span>hello world</span>
// Parsed as equivalent to
<span>{Jsx.string("hello world")}</span>
// Input
<> fragment text </>
// Parsed as equivalent to
<> {Jsx.string("fragment text")} </>
// Input
<div>first<span>nested</span>last</div>
// Parsed as equivalent to
<div>{Jsx.string("first")}<span>{Jsx.string("nested")}</span>{Jsx.string("last")}</div>
Backward Compatibility
- Existing explicitly wrapped strings continue to work unchanged:
<span>{Jsx.string("hello")}</span> - Expressions in braces remain unaffected:
<span>{someVariable}</span> - Only bare string literals get automatically wrapped
Test Cases
Added comprehensive test cases to tests/syntax_tests/data/parsing/grammar/expressions/jsx.res covering:
- Basic string literals in JSX elements
- String literals in JSX fragments
- Multiple string literals with nested elements
- Mixed content scenarios
The implementation follows established patterns in the ReScript compiler codebase and maintains full compatibility with existing JSX usage patterns.
[!WARNING]
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
esm.ubuntu.com
- Triggering command:
/usr/lib/apt/methods/https(dns block)opam.ocaml.org
- Triggering command:
/usr/bin/curl --write-out %{http_code}\n --retry 3 --retry-delay 2 --user-agent opam/2.1.5 -L -o /tmp/opam-5289-e2dcfc/index.tar.gz.part -- REDACTED(dns block)If you need me to access, download, or install something from one of these locations, you can either:
- Configure Actions setup steps to set up my environment, which run before the firewall is enabled
- Add the appropriate URLs or hosts to the custom allowlist in this repository's Copilot coding agent settings (admins only)
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.
@tsnobip I have doing some work in this area over in https://github.com/rescript-lang/rescript/compare/master...nojaf:rescript:jsx2
@nojaf nice! The handling of whitespace inside HTML/JSX is such a mess though, good luck with that! I'll then focus on wrapping variables inside curly braces for now then. This way, users of V12 will have their projects automatically formatted and we can introduce your work in v12.1 (or as soon as it's ready!)!