Skip to content

Commit 24585a5

Browse files
committed
fix(optimizer): support wrapping logical expressions in templates
1 parent c3f45f0 commit 24585a5

File tree

5 files changed

+142
-7
lines changed

5 files changed

+142
-7
lines changed

.changeset/shy-walls-shake.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
'@qwik.dev/core': patch
33
---
44

5-
fix: support wrapping type-asserted variables in templates
5+
fix: reactivity for type-asserted variables in templates

.changeset/some-birds-juggle.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@qwik.dev/core': patch
3+
---
4+
5+
fix: reactivity for logical expressions in templates

packages/qwik/src/optimizer/core/src/inlined_fn.rs

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -213,15 +213,54 @@ struct ObjectUsageChecker<'a> {
213213
used_as_object: bool,
214214
}
215215

216-
impl<'a> Visit for ObjectUsageChecker<'a> {
217-
fn visit_member_expr(&mut self, node: &ast::MemberExpr) {
218-
if let ast::Expr::Ident(obj_ident) = &*node.obj {
219-
for id in self.identifiers {
220-
if obj_ident.sym == id.0 {
216+
impl<'a> ObjectUsageChecker<'a> {
217+
// Helper function to recursively check if an expression contains one of the target identifiers.
218+
fn recursively_check_object_expr(&mut self, expr: &ast::Expr) {
219+
if self.used_as_object {
220+
return; // Already found
221+
}
222+
match expr {
223+
ast::Expr::Ident(ident) => {
224+
// Check if this identifier is one of the target identifiers
225+
if self
226+
.identifiers
227+
.iter()
228+
.any(|id| id.0 == ident.sym /* && id.1 == ident.ctxt */)
229+
{
221230
self.used_as_object = true;
222-
return;
223231
}
224232
}
233+
ast::Expr::Bin(bin_expr) => {
234+
// If it's a binary expression, specifically look for logical OR
235+
if bin_expr.op == ast::BinaryOp::LogicalOr {
236+
self.recursively_check_object_expr(&bin_expr.left);
237+
if self.used_as_object {
238+
return;
239+
}
240+
self.recursively_check_object_expr(&bin_expr.right);
241+
}
242+
}
243+
ast::Expr::Paren(paren_expr) => {
244+
// If it's a parenthesized expression, check the inner expression
245+
self.recursively_check_object_expr(&paren_expr.expr);
246+
}
247+
_ => {
248+
// For other expression types, traversal is handled by the Visit trait
249+
}
250+
}
251+
}
252+
}
253+
254+
impl<'a> Visit for ObjectUsageChecker<'a> {
255+
fn visit_member_expr(&mut self, node: &ast::MemberExpr) {
256+
if self.used_as_object {
257+
return;
258+
}
259+
260+
self.recursively_check_object_expr(&node.obj);
261+
262+
if self.used_as_object {
263+
return;
225264
}
226265
node.visit_children_with(self);
227266
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
source: packages/qwik/src/optimizer/core/src/test.rs
3+
assertion_line: 4405
4+
expression: output
5+
snapshot_kind: text
6+
---
7+
==INPUT==
8+
9+
10+
import { component$, useSignal } from '@qwik.dev/core';
11+
12+
export default component$(() => {
13+
const count = useSignal(0);
14+
const count2 = useSignal(0);
15+
return (
16+
<div>
17+
{(count || count2).value}
18+
</div>
19+
);
20+
});
21+
22+
============================= test.js ==
23+
24+
import { componentQrl } from "@qwik.dev/core";
25+
import { qrl } from "@qwik.dev/core";
26+
export default /*#__PURE__*/ componentQrl(/*#__PURE__*/ qrl(()=>import("./test.tsx_test_component_LUXeXe0DQrg"), "test_component_LUXeXe0DQrg"));
27+
28+
29+
Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;AAGE,6BAAe,mHAQZ\"}")
30+
============================= test.tsx_test_component_LUXeXe0DQrg.js (ENTRY POINT)==
31+
32+
import { _fnSignal } from "@qwik.dev/core";
33+
import { _jsxSorted } from "@qwik.dev/core";
34+
import { useSignal } from "@qwik.dev/core";
35+
export const test_component_LUXeXe0DQrg = ()=>{
36+
const count = useSignal(0);
37+
const count2 = useSignal(0);
38+
return /*#__PURE__*/ _jsxSorted("div", null, null, _fnSignal((p0, p1)=>(p0 || p1).value, [
39+
count,
40+
count2
41+
], "(p0||p1).value"), 3, "u6_0");
42+
};
43+
44+
45+
Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;0CAG4B;IACzB,MAAM,QAAQ,UAAU;IACxB,MAAM,SAAS,UAAU;IACzB,qBACC,WAAC,uCACC,CAAC,QAAe,EAAE,KAAK;;;;AAG3B\"}")
46+
/*
47+
{
48+
"origin": "test.tsx",
49+
"name": "test_component_LUXeXe0DQrg",
50+
"entry": null,
51+
"displayName": "test.tsx_test_component",
52+
"hash": "LUXeXe0DQrg",
53+
"canonicalFilename": "test.tsx_test_component_LUXeXe0DQrg",
54+
"path": "",
55+
"extension": "js",
56+
"parent": null,
57+
"ctxKind": "function",
58+
"ctxName": "component$",
59+
"captures": false,
60+
"loc": [
61+
89,
62+
233
63+
]
64+
}
65+
*/
66+
== DIAGNOSTICS ==
67+
68+
[]

packages/qwik/src/optimizer/core/src/test.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4377,6 +4377,29 @@ fn should_wrap_type_asserted_variables_in_template() {
43774377
});
43784378
}
43794379

4380+
#[test]
4381+
fn should_wrap_logical_expression_in_template() {
4382+
test_input!(TestInput {
4383+
code: r#"
4384+
import { component$, useSignal } from '@qwik.dev/core';
4385+
4386+
export default component$(() => {
4387+
const count = useSignal(0);
4388+
const count2 = useSignal(0);
4389+
return (
4390+
<div>
4391+
{(count || count2).value}
4392+
</div>
4393+
);
4394+
});
4395+
"#
4396+
.to_string(),
4397+
transpile_ts: true,
4398+
transpile_jsx: true,
4399+
..TestInput::default()
4400+
});
4401+
}
4402+
43804403
// TODO(misko): Make this test work by implementing strict serialization.
43814404
// #[test]
43824405
// fn example_of_synchronous_qrl_that_cant_be_serialized() {

0 commit comments

Comments
 (0)