Skip to content

feat: add macro.jsxRuntime option to emit Solid components from <Trans> - #286

Merged
andrii-bodnar merged 2 commits into
lingui:mainfrom
Chudesnov:feat/jsx-runtime-solid
Oct 19, 2026
Merged

andrii-bodnar merged 2 commits into
lingui:mainfrom
Chudesnov:feat/jsx-runtime-solid

Conversation

@Chudesnov

Copy link
Copy Markdown
Contributor

Summary

Adds a jsxRuntime: "react" | "solid" macro option (default "react").

With "solid", every JSX element inside <Trans> is emitted as a component function instead of a bare element:

// input
<Trans>Hello <a href="/docs">docs</a>.</Trans>

// jsxRuntime: "solid"
<Trans_ {.../*i18n*/ {
    id: "lqG909",
    components: {
        0: (_props)=><a href="/docs" {..._props}/>
    },
    message: "Hello <0>docs</0>."
}}/>

This ports the Babel macro's wrapJsxElementAsComponent behavior, and the id lqG909 matches the Babel snapshot should generate Solid components 1.

Details:

  • Original attributes are kept in source order, including existing spreads; {..._props} is appended last so runtime props such as children win. The tag name is cloned as-is (member expressions like <Foo.Bar> work), and the element is always self-closing.
  • _props is a fresh private_ident!, so hygiene renames it (_props1) if the user already has a _props binding in scope.
  • Only the <Trans> path is affected. t, msg, defineMessage and useLingui produce identical output in both modes.
  • Dedup of named placeholders (_t="link") and the "Multiple distinct JSX elements with the same placeholder name" check still compare the original elements, not the wrapped ones.

Docs: new jsxRuntime entry in both the @lingui/swc-plugin and @lingui/native-tools READMEs, pointing to defineConfig from @lingui/solid/config and the Solid tutorial.

Closes #276

Tests

  • New crates/lingui_macro/tests/jsx_solid.rs (snapshots): basic attrs, self-closing, nested elements with values, member-expression tag, existing spread attrs, reused named placeholder, Plural inside Trans, element inside an ICU case, _props name collision, conflicting named placeholders (to_panic!), and t/useLingui unaffected.
  • Unit tests for parsing jsxRuntime in options.rs ("solid", "react", missing, invalid).
  • native-tools spec for solid vs. default output; e2e test that macro.jsxRuntime from a Lingui config reaches the plugin options.
  • Ran locally on the final commit: cargo test, cargo fmt --check, cargo clippy --all-targets --all-features -- -D warnings, and native-tools yarn build && yarn test. No existing snapshots changed.

AI disclosure

Heads up: this was written with Claude Code. I spot-checked it, but I haven't written Rust before, so I'd appreciate a close look at component_value in builder.rs and the option plumbing in options.rs.

@Chudesnov
Chudesnov force-pushed the feat/jsx-runtime-solid branch from 3adde38 to 5ca423a Compare October 16, 2026 15:27
With `jsxRuntime: "solid"` every JSX element inside <Trans> is emitted as
`(_props) => <el {...attrs} {..._props}/>`, with the `_props` spread last so
runtime props such as `children` win. `@lingui/solid` renders components via
`createComponent`, so a bare element would not receive its children.

This matches wrapJsxElementAsComponent in the Babel macro. `t`, `msg`,
`defineMessage` and `useLingui` are unaffected. The default stays "react".

Closes lingui#276
@Chudesnov
Chudesnov force-pushed the feat/jsx-runtime-solid branch from 5ca423a to ce40927 Compare October 16, 2026 15:33
@codecov

codecov Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.60%. Comparing base (c538858) to head (07b146b).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #286      +/-   ##
==========================================
+ Coverage   94.42%   94.60%   +0.17%     
==========================================
  Files          11       11              
  Lines        2530     2576      +46     
==========================================
+ Hits         2389     2437      +48     
+ Misses        141      139       -2     
Flag Coverage Δ
unittests 94.60% <100.00%> (+0.17%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
crates/lingui_macro/src/builder.rs 99.16% <100.00%> (+0.07%) ⬆️
crates/lingui_macro/src/lib.rs 91.70% <ø> (+0.25%) ⬆️
crates/lingui_macro/src/options.rs 100.00% <100.00%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

Static review identified no blocking issues, and supplied regression coverage addresses option forwarding, component generation, and preserved default behavior.

No open issues 🎉

What changed in this PR

Adds Solid-compatible JSX output to Lingui’s macro tooling, addressing #276 while preserving React as the default.

Changes:

  • Adds and forwards the jsxRuntime option.
  • Wraps nested JSX elements as component functions in Solid mode.
  • Adds documentation and regression coverage for configuration, output, and placeholder handling.
File Description
packages/​native-tools/​README.md Documents Solid configuration.
packages/​native-tools/​__test__/​transform.spec.ts Tests React and Solid output.
packages/​lingui-macro/​src-js/​map-options.ts Types and forwards jsxRuntime.
packages/​lingui-macro/​README.md Documents runtime behavior.
packages/​lingui-macro/​e2e/​options.test.ts Tests configuration forwarding.
packages/​lingui-macro/​e2e/​fixtures/​lingui-options/​solid.config.js Adds Solid configuration fixture.
crates/​lingui_macro/​tests/​snapshots/​jsx_solid__solid_self_closing_element.snap Covers self-closing elements.
crates/​lingui_macro/​tests/​snapshots/​jsx_solid__solid_props_name_collision.snap Covers generated parameter hygiene.
crates/​lingui_macro/​tests/​snapshots/​jsx_solid__solid_plural_inside_trans.snap Covers nested plural macros.
crates/​lingui_macro/​tests/​snapshots/​jsx_solid__solid_nested_elements_and_values.snap Covers nesting and interpolation.
crates/​lingui_macro/​tests/​snapshots/​jsx_solid__solid_named_placeholder_reused.snap Covers placeholder reuse.
crates/​lingui_macro/​tests/​snapshots/​jsx_solid__solid_member_expression_tag.snap Covers member-expression tags.
crates/​lingui_macro/​tests/​snapshots/​jsx_solid__solid_existing_spread_attrs.snap Covers attribute spread ordering.
crates/​lingui_macro/​tests/​snapshots/​jsx_solid__solid_element_inside_icu_case.snap Covers elements within plural cases.
crates/​lingui_macro/​tests/​snapshots/​jsx_solid__solid_does_not_affect_t_and_use_lingui.snap Confirms unchanged non-JSX macro output.
crates/​lingui_macro/​tests/​snapshots/​jsx_solid__solid_distinct_elements_same_placeholder.snap Covers conflicting placeholder errors.
crates/​lingui_macro/​tests/​snapshots/​jsx_solid__solid_basic_component_with_attrs.snap Covers basic Solid component output.
crates/​lingui_macro/​tests/​jsx_solid.rs Adds Solid transformation regression tests.
crates/​lingui_macro/​src/​options.rs Defines, defaults, and tests runtime options.
crates/​lingui_macro/​src/​lib.rs Exports JsxRuntime.
crates/​lingui_macro/​src/​builder.rs Generates Solid component wrappers.

Review effort: Balanced

@andrii-bodnar
andrii-bodnar merged commit 67618ff into lingui:main Oct 19, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support macro.jsxRuntime: "solid" for the <Trans> transform

4 participants