fix: parenthesize a unary base in the compiled ** expression
All Submissions:
- Have you followed the guidelines stated in CONTRIBUTING.md file?
- Have you checked to ensure there aren't any other open Pull Requests for the desired changed?
Type of change
- Bug fix (non-breaking change which fixes an issue)
Changes To Core Features:
- Have you added an explanation of what your changes do and why you'd like us to include them?
- Have you written new tests for your core changes, as applicable?
- Have you successfully ran tests with your changes locally?
Description
number_exponent_operation renders ({lhs} ** {rhs}) as is. JavaScript doesn't allow a unary expression as the base of ** (-2 ** 2 is a SyntaxError, unlike Python where it means -(2 ** 2)), so when the base is a negative literal or a negated var the compiled page doesn't build. For example (-State.velocity) ** 2 renders as (-(…velocity…) ** 2) and rx.Var.create(-2) ** State.n as (-2 ** …n…).
The base is now parenthesized when it starts with a unary operator, so those become ((-(…velocity…)) ** 2) and ((-2) ** …n…). Everything else renders exactly as before ((1 ** 2), (x ** -1), and -(x ** 2) from -x ** 2), so no existing snapshot changes.
Before / After with a one-page app, rx.text("squared: ", (-State.velocity) ** 2), running reflex export --frontend-only:
# before (main)
error: Build failed with 1 error:
[builtin:vite-transform] A unary expression with the '-' operator cannot
be used as the left operand of an exponentiation expression
╭─[ app_components/powapp/powapp.jsx:21:10 ]
21 │ (-(reflex___state____state__powapp___powapp____state.velocity_rx_state_) ** 2)
│ Help: Wrap the unary expression in parentheses to make the precedence explicit
exit=1
# after
Creating Production Build: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 4/4
exit=0
I also ran the rendered expressions through Node and they match Python: ((-(x)) ** 2) is 9, ((-2) ** x) is -8, ((-2.5) ** 2) is 6.25 for x = 3.
test_exponent_parenthesizes_a_unary_base covers the negative-literal and negated-var bases and the cases that must stay unwrapped, and fails on main with '(-2 ** 3)' == '((-2) ** 3)'. tests/units passes (11965 passed), along with ruff and pyright on the changed files.