From d2b0b64b9f4447bc6aab4453b7d3b0afe853a4af Mon Sep 17 00:00:00 2001 From: Alessandro Gimona Date: Fri, 22 Nov 2024 06:25:52 +0100 Subject: [PATCH 01/11] add v2 test - should update elements correctly --- .../qwik/src/core/tests/use-resource.spec.tsx | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/packages/qwik/src/core/tests/use-resource.spec.tsx b/packages/qwik/src/core/tests/use-resource.spec.tsx index 9b67131dfbe..707e40bba77 100644 --- a/packages/qwik/src/core/tests/use-resource.spec.tsx +++ b/packages/qwik/src/core/tests/use-resource.spec.tsx @@ -339,4 +339,98 @@ describe.each([ ); }); + + it('should update elements correctly', async () => { + (global as any).delay = () => new Promise((res) => ((global as any).delay.resolve = res)); + const ResourceCmp = component$(() => { + const count = useSignal(0); + const resource = useResource$(async ({ track }) => { + track(count); + return count.value + 10; + }); + + return ( + <> + +

Loading..

} + onRejected={() =>

error ...

} + onResolved={(data) => ( + <> +
{data}
+ + + )} + /> + + ); + }); + + const { vNode, container } = await render(, { debug }); + expect(vNode).toMatchVDOM( + + + + + + + +
10
+ +
+
+
+
+
+
+ ); + await trigger(container.element, 'button', 'click'); + + expect(vNode).toMatchVDOM( + + + + + + + +
11
+ +
+
+
+
+
+
+ ); + // await (global as any).delay.resolve(); + await getTestPlatform().flush(); + + expect(vNode).toMatchVDOM( + + + + + + + +
11
+ +
+
+
+
+
+
+ ); + (global as any).delay = undefined; + }); }); From 3c17163df40c9d79754b73199fff9bb49fb9cbcc Mon Sep 17 00:00:00 2001 From: Varixo Date: Fri, 29 Nov 2024 23:10:58 +0100 Subject: [PATCH 02/11] fix client rendering problem, simplify test --- .../qwik/src/core/tests/use-resource.spec.tsx | 28 ++----------------- packages/qwik/src/core/use/use-resource.ts | 7 +++-- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/packages/qwik/src/core/tests/use-resource.spec.tsx b/packages/qwik/src/core/tests/use-resource.spec.tsx index 707e40bba77..53b0eb21c24 100644 --- a/packages/qwik/src/core/tests/use-resource.spec.tsx +++ b/packages/qwik/src/core/tests/use-resource.spec.tsx @@ -340,8 +340,7 @@ describe.each([ ); }); - it('should update elements correctly', async () => { - (global as any).delay = () => new Promise((res) => ((global as any).delay.resolve = res)); + it('should update elements correctly inside onResolved fn', async () => { const ResourceCmp = component$(() => { const count = useSignal(0); const resource = useResource$(async ({ track }) => { @@ -360,7 +359,7 @@ describe.each([ onResolved={(data) => ( <>
{data}
- + )} /> @@ -409,28 +408,5 @@ describe.each([ ); - // await (global as any).delay.resolve(); - await getTestPlatform().flush(); - - expect(vNode).toMatchVDOM( - - - - - - - -
11
- -
-
-
-
-
-
- ); - (global as any).delay = undefined; }); }); diff --git a/packages/qwik/src/core/use/use-resource.ts b/packages/qwik/src/core/use/use-resource.ts index 216fcc81421..ccd194c3360 100644 --- a/packages/qwik/src/core/use/use-resource.ts +++ b/packages/qwik/src/core/use/use-resource.ts @@ -195,8 +195,11 @@ function getResourceValueAsPromise(props: ResourceProps): Promise resource._resolved) as T).then(props.onResolved); + const resolvedValue = untrack(() => resource._resolved) as T; + if (resolvedValue !== undefined) { + // resolved, pending without onPending prop or rejected with onRejected prop + return Promise.resolve(resolvedValue).then(props.onResolved); + } } } return resource.value.then( From 7f1bfe0984e34756ce65a50372f0c59b7fcb2aa3 Mon Sep 17 00:00:00 2001 From: Varixo Date: Fri, 6 Dec 2024 10:24:12 +0100 Subject: [PATCH 03/11] fix optimizer is_constant --- .../src/optimizer/core/benches/transform.rs | 1 + .../qwik/src/optimizer/core/src/transform.rs | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/qwik/src/optimizer/core/benches/transform.rs b/packages/qwik/src/optimizer/core/benches/transform.rs index c913f19cd6f..845710e9009 100644 --- a/packages/qwik/src/optimizer/core/benches/transform.rs +++ b/packages/qwik/src/optimizer/core/benches/transform.rs @@ -188,6 +188,7 @@ fn transform_todo_app(b: &mut Bencher) { input: vec![TransformModuleInput { code: code.into(), path: "file.tsx".into(), + dev_path: None, }], root_dir: None, core_module: None, diff --git a/packages/qwik/src/optimizer/core/src/transform.rs b/packages/qwik/src/optimizer/core/src/transform.rs index bdd95719a38..c2332313411 100644 --- a/packages/qwik/src/optimizer/core/src/transform.rs +++ b/packages/qwik/src/optimizer/core/src/transform.rs @@ -1787,10 +1787,15 @@ impl<'a> Fold for QwikTransform<'a> { .last_mut() .expect("Declaration stack empty!"); + let is_qcomponent = self.stack_ctxt.last() == Some(&QCOMPONENT.to_string()); + for param in &node.params { let mut identifiers = vec![]; - collect_from_pat(¶m.pat, &mut identifiers); - let is_constant = matches!(param.pat, ast::Pat::Ident(_)); + let is_identifier = collect_from_pat(¶m.pat, &mut identifiers); + let mut is_constant = false; + if is_qcomponent { + is_constant = is_identifier; + } current_scope.extend( identifiers .into_iter() @@ -1817,10 +1822,16 @@ impl<'a> Fold for QwikTransform<'a> { .decl_stack .last_mut() .expect("Declaration stack empty!"); + + let is_qcomponent = self.stack_ctxt.last() == Some(&QCOMPONENT.to_string()); + for param in &node.params { let mut identifiers = vec![]; - collect_from_pat(param, &mut identifiers); - let is_constant = matches!(param, ast::Pat::Ident(_)); + let is_identifier = collect_from_pat(param, &mut identifiers); + let mut is_constant = false; + if is_qcomponent { + is_constant = is_identifier; + } current_scope.extend( identifiers .into_iter() From 1b54c66cfee26411ad8f4c2b6f591d4c138d713e Mon Sep 17 00:00:00 2001 From: Varixo Date: Wed, 18 Dec 2024 22:03:36 +0100 Subject: [PATCH 04/11] fix the optimizer props --- .../qwik/src/core/tests/component.spec.tsx | 8 +- .../src/core/tests/inline-component.spec.tsx | 24 +- .../qwik/src/core/tests/use-context.spec.tsx | 8 +- .../src/core/tests/use-lexical-scope.spec.tsx | 144 +++++++ .../qwik/src/core/tests/use-store.spec.tsx | 52 --- ...nent_with_event_listeners_inside_loop.snap | 407 ++++++++++++++++++ ..._test__example_functional_component_2.snap | 10 +- ...le_functional_component_capture_props.snap | 4 +- .../qwik_core__test__issue_5008.snap | 4 +- packages/qwik/src/optimizer/core/src/test.rs | 109 +++++ .../qwik/src/optimizer/core/src/transform.rs | 33 +- 11 files changed, 711 insertions(+), 92 deletions(-) create mode 100644 packages/qwik/src/core/tests/use-lexical-scope.spec.tsx create mode 100644 packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_component_with_event_listeners_inside_loop.snap diff --git a/packages/qwik/src/core/tests/component.spec.tsx b/packages/qwik/src/core/tests/component.spec.tsx index d7415111cdf..77658709753 100644 --- a/packages/qwik/src/core/tests/component.spec.tsx +++ b/packages/qwik/src/core/tests/component.spec.tsx @@ -709,7 +709,7 @@ describe.each([
{'Child '} - {'1'} + {'1'} {', active: '} {'false'}
@@ -717,7 +717,7 @@ describe.each([
{'Child '} - {'2'} + {'2'} {', active: '} {'true'}
@@ -736,7 +736,7 @@ describe.each([
{'Child '} - {'1'} + {'1'} {', active: '} {'true'}
@@ -744,7 +744,7 @@ describe.each([
{'Child '} - {'2'} + {'2'} {', active: '} {'false'}
diff --git a/packages/qwik/src/core/tests/inline-component.spec.tsx b/packages/qwik/src/core/tests/inline-component.spec.tsx index c428ee3eb7d..467f0afd508 100644 --- a/packages/qwik/src/core/tests/inline-component.spec.tsx +++ b/packages/qwik/src/core/tests/inline-component.spec.tsx @@ -258,13 +258,19 @@ describe.each([
-
qwik
+
+ qwik +
-
foo
+
+ foo +
-
bar
+
+ bar +
@@ -277,13 +283,19 @@ describe.each([
-
bar
+
+ bar +
-
foo
+
+ foo +
-
qwik
+
+ qwik +
diff --git a/packages/qwik/src/core/tests/use-context.spec.tsx b/packages/qwik/src/core/tests/use-context.spec.tsx index 90be1dccc8f..1c61fed8e3f 100644 --- a/packages/qwik/src/core/tests/use-context.spec.tsx +++ b/packages/qwik/src/core/tests/use-context.spec.tsx @@ -150,7 +150,9 @@ describe.each([ -

1

+

+ 1 +

0

@@ -176,7 +178,9 @@ describe.each([ -

1

+

+ 1 +

2

diff --git a/packages/qwik/src/core/tests/use-lexical-scope.spec.tsx b/packages/qwik/src/core/tests/use-lexical-scope.spec.tsx new file mode 100644 index 00000000000..42776c77b73 --- /dev/null +++ b/packages/qwik/src/core/tests/use-lexical-scope.spec.tsx @@ -0,0 +1,144 @@ +import { domRender, ssrRenderToDom, trigger } from '@qwik.dev/core/testing'; +import { describe, expect, it } from 'vitest'; +import { + component$, + useStore, + useSignal, + Fragment as Component, + Fragment as Signal, +} from '@qwik.dev/core'; + +const debug = false; //true; +Error.stackTraceLimit = 100; + +describe.each([ + { render: ssrRenderToDom }, // + { render: domRender }, // +])('$render.name: useLexicalScope', ({ render }) => { + it('should update const prop event value', async () => { + type Cart = string[]; + + const Parent = component$(() => { + const cart = useStore([]); + const results = useSignal(['foo']); + + return ( +
+ + + {results.value.map((item) => ( + + ))} +
    + {cart.map((item) => ( +
  • + {item} +
  • + ))} +
+
+ ); + }); + + const { vNode, document } = await render(, { debug }); + + expect(vNode).toMatchVDOM( + +
+ + +
    +
    +
    + ); + + await trigger(document.body, 'button#first', 'click'); + + expect(vNode).toMatchVDOM( + +
    + + +
      +
      +
      + ); + + await trigger(document.body, 'button#second', 'click'); + + expect(vNode).toMatchVDOM( + +
      + + +
        +
      • + item +
      • +
      +
      +
      + ); + }); + + describe('regression', () => { + it('#5662 - should update value in the list', async () => { + /** + * ROOT CAUSE ANALYSIS: This is a bug in Optimizer. The optimizer incorrectly marks the + * `onClick` listener as 'const'/'immutable'. Because it is const, the QRL associated with the + * click handler always points to the original object, and it is not updated. + */ + const Cmp = component$(() => { + const store = useStore<{ users: { name: string }[] }>({ users: [{ name: 'Giorgio' }] }); + + return ( +
      + {store.users.map((user, key) => ( + { + store.users = store.users.map(({ name }: { name: string }) => ({ + name: name === user.name ? name + '!' : name, + })); + }} + > + {user.name} + + ))} +
      + ); + }); + const { vNode, container } = await render(, { debug }); + expect(vNode).toMatchVDOM( + +
      + + {'Giorgio'} + +
      +
      + ); + await trigger(container.element, 'span', 'click'); + await trigger(container.element, 'span', 'click'); + await trigger(container.element, 'span', 'click'); + await trigger(container.element, 'span', 'click'); + await trigger(container.element, 'span', 'click'); + expect(vNode).toMatchVDOM( + +
      + + {'Giorgio!!!!!'} + +
      +
      + ); + }); + }); +}); diff --git a/packages/qwik/src/core/tests/use-store.spec.tsx b/packages/qwik/src/core/tests/use-store.spec.tsx index 5115ca262bc..46ef904c3fa 100644 --- a/packages/qwik/src/core/tests/use-store.spec.tsx +++ b/packages/qwik/src/core/tests/use-store.spec.tsx @@ -947,58 +947,6 @@ describe.each([ vi.useRealTimers(); }); - it.skip('#5662 - should update value in the list', async () => { - /** - * ROOT CAUSE ANALYSIS: This is a bug in Optimizer. The optimizer incorrectly marks the - * `onClick` listener as 'const'/'immutable'. Because it is const, the QRL associated with the - * click handler always points to the original object, and it is not updated. - */ - const Cmp = component$(() => { - const store = useStore<{ users: { name: string }[] }>({ users: [{ name: 'Giorgio' }] }); - - return ( -
      - {store.users.map((user, key) => ( - { - store.users = store.users.map(({ name }: { name: string }) => ({ - name: name === user.name ? name + '!' : name, - })); - }} - > - {user.name} - - ))} -
      - ); - }); - const { vNode, container } = await render(, { debug }); - expect(vNode).toMatchVDOM( - -
      - - {'Giorgio'} - -
      -
      - ); - await trigger(container.element, 'span', 'click'); - await trigger(container.element, 'span', 'click'); - await trigger(container.element, 'span', 'click'); - await trigger(container.element, 'span', 'click'); - await trigger(container.element, 'span', 'click'); - expect(vNode).toMatchVDOM( - -
      - - {'Giorgio!!!!!'} - -
      -
      - ); - }); - it('#5017 - should update child nodes for direct array', async () => { const Child = component$<{ columns: string }>(({ columns }) => { return
      Child: {columns}
      ; diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_component_with_event_listeners_inside_loop.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_component_with_event_listeners_inside_loop.snap new file mode 100644 index 00000000000..6fda7cbb190 --- /dev/null +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_component_with_event_listeners_inside_loop.snap @@ -0,0 +1,407 @@ +--- +source: packages/qwik/src/optimizer/core/src/test.rs +assertion_line: 3850 +expression: output +snapshot_kind: text +--- +==INPUT== + + +import { $, component$, useStore, useSignal } from '@qwik.dev/core'; +export const App = component$(() => { + const cart = useStore([]); + const results = useSignal(['foo']); + function loopArrowFn(results: string[]) { + return results.map((item) => ( + { + cart.push(item); + }} + > + {item} + + )); + } + function loopForI(results: string[]) { + const items = []; + for (let i = 0; i < results.length; i++) { + items.push( + { + cart.push(results[i]); + }} + > + {results[i]} + + ); + } + return items; + } + function loopForOf(results: string[]) { + const items = []; + for (const item of results) { + items.push( + { + cart.push(item); + }} + > + {item} + + ); + } + return items; + } + function loopForIn(results: string[]) { + const items = []; + for (const key in results) { + items.push( + { + cart.push(results[key]); + }} + > + {results[key]} + + ); + } + return items; + } + function loopWhile(results: string[]) { + const items = []; + let i = 0; + while (i < results.length) { + items.push( + { + cart.push(results[i]); + }} + > + {results[i]} + + ); + i++; + } + return items; + } + return ( +
      + {results.value.map((item) => ( + + ))} + {loopArrowFn(results.value)} + {loopForI(results.value)} + {loopForOf(results.value)} + {loopForIn(results.value)} + {loopWhile(results.value)} +
      + ); + }); + +============================= test.js == + +import { componentQrl } from "@qwik.dev/core"; +import { qrl } from "@qwik.dev/core"; +export const App = /*#__PURE__*/ componentQrl(/*#__PURE__*/ qrl(()=>import("./test.tsx_App_component_ckEPmXZlub0"), "App_component_ckEPmXZlub0")); + + +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;AAEA,OAAO,MAAM,oBAAM,iHA+FZ\"}") +============================= test.tsx_App_component_loopWhile_span_onClick_ycCPdh6iazg.js (ENTRY POINT)== + +import { useLexicalScope } from "@qwik.dev/core"; +export const App_component_loopWhile_span_onClick_ycCPdh6iazg = ()=>{ + const [cart, i, results] = useLexicalScope(); + cart.push(results[i]); +}; + + +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";gEAmEwB;;IACR,KAAK,IAAI,CAAC,OAAO,CAAC,EAAE\"}") +/* +{ + "origin": "test.tsx", + "name": "App_component_loopWhile_span_onClick_ycCPdh6iazg", + "entry": null, + "displayName": "test.tsx_App_component_loopWhile_span_onClick", + "hash": "ycCPdh6iazg", + "canonicalFilename": "test.tsx_App_component_loopWhile_span_onClick_ycCPdh6iazg", + "path": "", + "extension": "js", + "parent": "App_component_ckEPmXZlub0", + "ctxKind": "eventHandler", + "ctxName": "onClick$", + "captures": true, + "loc": [ + 1693, + 1755 + ] +} +*/ +============================= test.tsx_App_component_loopArrowFn_span_onClick_xyQUo7XwaEM.js (ENTRY POINT)== + +import { useLexicalScope } from "@qwik.dev/core"; +export const App_component_loopArrowFn_span_onClick_xyQUo7XwaEM = ()=>{ + const [cart, item] = useLexicalScope(); + cart.push(item); +}; + + +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";kEAQsB;;IACR,KAAK,IAAI,CAAC\"}") +/* +{ + "origin": "test.tsx", + "name": "App_component_loopArrowFn_span_onClick_xyQUo7XwaEM", + "entry": null, + "displayName": "test.tsx_App_component_loopArrowFn_span_onClick", + "hash": "xyQUo7XwaEM", + "canonicalFilename": "test.tsx_App_component_loopArrowFn_span_onClick_xyQUo7XwaEM", + "path": "", + "extension": "js", + "parent": "App_component_ckEPmXZlub0", + "ctxKind": "eventHandler", + "ctxName": "onClick$", + "captures": true, + "loc": [ + 319, + 371 + ] +} +*/ +============================= test.tsx_App_component_loopForIn_span_onClick_1mDJD4xhUZ8.js (ENTRY POINT)== + +import { useLexicalScope } from "@qwik.dev/core"; +export const App_component_loopForIn_span_onClick_1mDJD4xhUZ8 = ()=>{ + const [cart, key, results] = useLexicalScope(); + cart.push(results[key]); +}; + + +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";gEAmDwB;;IACR,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI\"}") +/* +{ + "origin": "test.tsx", + "name": "App_component_loopForIn_span_onClick_1mDJD4xhUZ8", + "entry": null, + "displayName": "test.tsx_App_component_loopForIn_span_onClick", + "hash": "1mDJD4xhUZ8", + "canonicalFilename": "test.tsx_App_component_loopForIn_span_onClick_1mDJD4xhUZ8", + "path": "", + "extension": "js", + "parent": "App_component_ckEPmXZlub0", + "ctxKind": "eventHandler", + "ctxName": "onClick$", + "captures": true, + "loc": [ + 1319, + 1383 + ] +} +*/ +============================= test.tsx_App_component_loopForOf_span_onClick_AjlGKUbcCKY.js (ENTRY POINT)== + +import { useLexicalScope } from "@qwik.dev/core"; +export const App_component_loopForOf_span_onClick_AjlGKUbcCKY = ()=>{ + const [cart, item] = useLexicalScope(); + cart.push(item); +}; + + +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";gEAoCwB;;IACR,KAAK,IAAI,CAAC\"}") +/* +{ + "origin": "test.tsx", + "name": "App_component_loopForOf_span_onClick_AjlGKUbcCKY", + "entry": null, + "displayName": "test.tsx_App_component_loopForOf_span_onClick", + "hash": "AjlGKUbcCKY", + "canonicalFilename": "test.tsx_App_component_loopForOf_span_onClick_AjlGKUbcCKY", + "path": "", + "extension": "js", + "parent": "App_component_ckEPmXZlub0", + "ctxKind": "eventHandler", + "ctxName": "onClick$", + "captures": true, + "loc": [ + 980, + 1036 + ] +} +*/ +============================= test.tsx_App_component_div_button_onClick_f5NwW9e63a4.js (ENTRY POINT)== + +import { useLexicalScope } from "@qwik.dev/core"; +export const App_component_div_button_onClick_f5NwW9e63a4 = ()=>{ + const [cart, item] = useLexicalScope(); + cart.push(item); +}; + + +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";4DAmFwB;;IACR,KAAK,IAAI,CAAC\"}") +/* +{ + "origin": "test.tsx", + "name": "App_component_div_button_onClick_f5NwW9e63a4", + "entry": null, + "displayName": "test.tsx_App_component_div_button_onClick", + "hash": "f5NwW9e63a4", + "canonicalFilename": "test.tsx_App_component_div_button_onClick_f5NwW9e63a4", + "path": "", + "extension": "js", + "parent": "App_component_ckEPmXZlub0", + "ctxKind": "eventHandler", + "ctxName": "onClick$", + "captures": true, + "loc": [ + 2026, + 2082 + ] +} +*/ +============================= test.tsx_App_component_ckEPmXZlub0.js (ENTRY POINT)== + +import { _fnSignal } from "@qwik.dev/core"; +import { _jsxSorted } from "@qwik.dev/core"; +import { qrl } from "@qwik.dev/core"; +import { useSignal } from "@qwik.dev/core"; +import { useStore } from "@qwik.dev/core"; +export const App_component_ckEPmXZlub0 = ()=>{ + const cart = useStore([]); + const results = useSignal([ + 'foo' + ]); + function loopArrowFn(results) { + return results.map((item)=>/*#__PURE__*/ _jsxSorted("span", { + onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_App_component_loopArrowFn_span_onClick_xyQUo7XwaEM"), "App_component_loopArrowFn_span_onClick_xyQUo7XwaEM", [ + cart, + item + ]) + }, null, item, 0, "u6_0")); + } + function loopForI(results) { + const items = []; + for(let i = 0; i < results.length; i++)items.push(/*#__PURE__*/ _jsxSorted("span", { + onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_App_component_loopForI_span_onClick_MXjGbUTgkco"), "App_component_loopForI_span_onClick_MXjGbUTgkco", [ + cart, + i, + results + ]) + }, null, results[i], 0, "u6_1")); + return items; + } + function loopForOf(results) { + const items = []; + for (const item of results)items.push(/*#__PURE__*/ _jsxSorted("span", null, { + onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_App_component_loopForOf_span_onClick_AjlGKUbcCKY"), "App_component_loopForOf_span_onClick_AjlGKUbcCKY", [ + cart, + item + ]) + }, item, 3, "u6_2")); + return items; + } + function loopForIn(results) { + const items = []; + for(const key in results)items.push(/*#__PURE__*/ _jsxSorted("span", { + onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_App_component_loopForIn_span_onClick_1mDJD4xhUZ8"), "App_component_loopForIn_span_onClick_1mDJD4xhUZ8", [ + cart, + key, + results + ]) + }, null, _fnSignal((p0, p1)=>p1[p0], [ + key, + results + ], "p1[p0]"), 2, "u6_3")); + return items; + } + function loopWhile(results) { + const items = []; + let i = 0; + while(i < results.length){ + items.push(/*#__PURE__*/ _jsxSorted("span", { + onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_App_component_loopWhile_span_onClick_ycCPdh6iazg"), "App_component_loopWhile_span_onClick_ycCPdh6iazg", [ + cart, + i, + results + ]) + }, null, results[i], 0, "u6_4")); + i++; + } + return items; + } + return /*#__PURE__*/ _jsxSorted("div", null, null, [ + results.value.map((item)=>/*#__PURE__*/ _jsxSorted("button", { + onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_App_component_div_button_onClick_f5NwW9e63a4"), "App_component_div_button_onClick_f5NwW9e63a4", [ + cart, + item + ]) + }, { + id: "second" + }, item, 0, "u6_5")), + loopArrowFn(results.value), + loopForI(results.value), + loopForOf(results.value), + loopForIn(results.value), + loopWhile(results.value) + ], 1, "u6_6"); +}; + + +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;;;yCAE8B;IACxB,MAAM,OAAO,SAAmB,EAAE;IAClC,MAAM,UAAU,UAAU;QAAC;KAAM;IACjC,SAAS,YAAY,OAAiB;QACpC,OAAO,QAAQ,GAAG,CAAC,CAAC,qBAClB,WAAC;gBACC,QAAQ;;;;qBAIP;IAGP;IACA,SAAS,SAAS,OAAiB;QACjC,MAAM,QAAQ,EAAE;QAChB,IAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,MAAM,EAAE,IAClC,MAAM,IAAI,eACR,WAAC;YACC,QAAQ;;;;;iBAIP,OAAO,CAAC,EAAE;QAIjB,OAAO;IACT;IACA,SAAS,UAAU,OAAiB;QAClC,MAAM,QAAQ,EAAE;QAChB,KAAK,MAAM,QAAQ,QACjB,MAAM,IAAI,eACR,WAAC;YACC,QAAQ;;;;WAIP;QAIP,OAAO;IACT;IACA,SAAS,UAAU,OAAiB;QAClC,MAAM,QAAQ,EAAE;QAChB,IAAK,MAAM,OAAO,QAChB,MAAM,IAAI,eACR,WAAC;YACC,QAAQ;;;;;qCAIP,EAAO,IAAK;;;;QAInB,OAAO;IACT;IACA,SAAS,UAAU,OAAiB;QAClC,MAAM,QAAQ,EAAE;QAChB,IAAI,IAAI;QACR,MAAO,IAAI,QAAQ,MAAM,CAAE;YACzB,MAAM,IAAI,eACR,WAAC;gBACC,QAAQ;;;;;qBAIP,OAAO,CAAC,EAAE;YAGf;QACF;QACA,OAAO;IACT;IACA,qBACE,WAAC;QACE,QAAQ,KAAK,CAAC,GAAG,CAAC,CAAC,qBAClB,WAAC;gBAEC,QAAQ;;;;;gBADR,IAAG;eAKF;QAGJ,YAAY,QAAQ,KAAK;QACzB,SAAS,QAAQ,KAAK;QACtB,UAAU,QAAQ,KAAK;QACvB,UAAU,QAAQ,KAAK;QACvB,UAAU,QAAQ,KAAK;;AAG9B\"}") +/* +{ + "origin": "test.tsx", + "name": "App_component_ckEPmXZlub0", + "entry": null, + "displayName": "test.tsx_App_component", + "hash": "ckEPmXZlub0", + "canonicalFilename": "test.tsx_App_component_ckEPmXZlub0", + "path": "", + "extension": "js", + "parent": null, + "ctxKind": "function", + "ctxName": "component$", + "captures": false, + "loc": [ + 101, + 2370 + ] +} +*/ +============================= test.tsx_App_component_loopForI_span_onClick_MXjGbUTgkco.js (ENTRY POINT)== + +import { useLexicalScope } from "@qwik.dev/core"; +export const App_component_loopForI_span_onClick_MXjGbUTgkco = ()=>{ + const [cart, i, results] = useLexicalScope(); + cart.push(results[i]); +}; + + +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";+DAqBwB;;IACR,KAAK,IAAI,CAAC,OAAO,CAAC,EAAE\"}") +/* +{ + "origin": "test.tsx", + "name": "App_component_loopForI_span_onClick_MXjGbUTgkco", + "entry": null, + "displayName": "test.tsx_App_component_loopForI_span_onClick", + "hash": "MXjGbUTgkco", + "canonicalFilename": "test.tsx_App_component_loopForI_span_onClick_MXjGbUTgkco", + "path": "", + "extension": "js", + "parent": "App_component_ckEPmXZlub0", + "ctxKind": "eventHandler", + "ctxName": "onClick$", + "captures": true, + "loc": [ + 628, + 690 + ] +} +*/ +== DIAGNOSTICS == + +[] diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_functional_component_2.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_functional_component_2.snap index 666a9847a5b..46890060684 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_functional_component_2.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_functional_component_2.snap @@ -125,26 +125,26 @@ export const App_component_ckEPmXZlub0 = (props)=>{ thing: 0 }); const count2 = state.count * 2; - return /*#__PURE__*/ _jsxSorted("div", null, { + return /*#__PURE__*/ _jsxSorted("div", { onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_App_component_div_onClick_1CGetmFZx0g"), "App_component_div_onClick_1CGetmFZx0g", [ count2, state ]) - }, [ + }, null, [ /*#__PURE__*/ _jsxSorted("span", null, null, _wrapProp(state, "count"), 3, null), - buttons.map((btn)=>/*#__PURE__*/ _jsxSorted("button", null, { + buttons.map((btn)=>/*#__PURE__*/ _jsxSorted("button", { onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_App_component_div_button_onClick_f5NwW9e63a4"), "App_component_div_button_onClick_f5NwW9e63a4", [ btn, props, state, thing ]) - }, _wrapProp(btn, "name"), 3, "u6_0")) + }, null, _wrapProp(btn, "name"), 0, "u6_0")) ], 0, "u6_1"); }; -Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;;;yCAQ8B,CAAC;IAC9B,MAAM,QAAQ;IACd,MAAM,QAAQ,SAAS;QAAC,OAAO;IAAC;IAGhC,MAAM,SAAS,MAAM,KAAK,GAAG;IAC7B,qBACC,WAAC;QAAI,QAAQ;;;;;sBACZ,WAAC,8BAAM;QACN,QAAQ,GAAG,CAAC,CAAA,oBACZ,WAAC;gBACA,QAAQ;;;;;;yBAEP;;AAON\"}") +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;;;yCAQ8B,CAAC;IAC9B,MAAM,QAAQ;IACd,MAAM,QAAQ,SAAS;QAAC,OAAO;IAAC;IAGhC,MAAM,SAAS,MAAM,KAAK,GAAG;IAC7B,qBACC,WAAC;QAAI,QAAQ;;;;;sBACZ,WAAC,8BAAM;QACN,QAAQ,GAAG,CAAC,CAAA,oBACZ,WAAC;gBACA,QAAQ;;;;;;+BAEP;;AAON\"}") /* { "origin": "test.tsx", diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_functional_component_capture_props.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_functional_component_capture_props.snap index 798cdd4e359..977501706d2 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_functional_component_capture_props.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_functional_component_capture_props.snap @@ -119,12 +119,12 @@ import { _jsxSorted } from "@qwik.dev/core"; import { qrl } from "@qwik.dev/core"; export const App_component_1_w0t0o3QMovU = ()=>{ const [C2, C3, C4, C5, C6, C7, C8, I2, I3, I4, I5, I6, I7, I8, count, state] = useLexicalScope(); - return /*#__PURE__*/ _jsxSorted("div", null, { + return /*#__PURE__*/ _jsxSorted("div", { onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_App_component_div_onClick_1CGetmFZx0g"), "App_component_div_onClick_1CGetmFZx0g", [ count, state ]) - }, [ + }, null, [ I2, I3, I4, diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__issue_5008.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__issue_5008.snap index 4d7d34bfb26..6047acc99c1 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__issue_5008.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__issue_5008.snap @@ -55,12 +55,12 @@ export const test_component_LUXeXe0DQrg = ()=>{ return /*#__PURE__*/ _jsxSorted("div", null, null, [ "Function: ", _wrapProp(v) - ], 3, "fn_" + idx); + ], 1, "fn_" + idx); }), store.map((v, idx)=>/*#__PURE__*/ _jsxSorted("div", null, null, [ "Arrow: ", _wrapProp(v) - ], 3, "arrow_" + idx)) + ], 1, "arrow_" + idx)) ], 1, "u6_0"); }; diff --git a/packages/qwik/src/optimizer/core/src/test.rs b/packages/qwik/src/optimizer/core/src/test.rs index f02a957c615..d4aa2e94172 100644 --- a/packages/qwik/src/optimizer/core/src/test.rs +++ b/packages/qwik/src/optimizer/core/src/test.rs @@ -3845,6 +3845,115 @@ fn rename_builder_io() { }); } +#[test] +fn example_component_with_event_listeners_inside_loop() { + test_input!(TestInput { + code: r#" +import { $, component$, useStore, useSignal } from '@qwik.dev/core'; +export const App = component$(() => { + const cart = useStore([]); + const results = useSignal(['foo']); + function loopArrowFn(results: string[]) { + return results.map((item) => ( + { + cart.push(item); + }} + > + {item} + + )); + } + function loopForI(results: string[]) { + const items = []; + for (let i = 0; i < results.length; i++) { + items.push( + { + cart.push(results[i]); + }} + > + {results[i]} + + ); + } + return items; + } + function loopForOf(results: string[]) { + const items = []; + for (const item of results) { + items.push( + { + cart.push(item); + }} + > + {item} + + ); + } + return items; + } + function loopForIn(results: string[]) { + const items = []; + for (const key in results) { + items.push( + { + cart.push(results[key]); + }} + > + {results[key]} + + ); + } + return items; + } + function loopWhile(results: string[]) { + const items = []; + let i = 0; + while (i < results.length) { + items.push( + { + cart.push(results[i]); + }} + > + {results[i]} + + ); + i++; + } + return items; + } + return ( +
      + {results.value.map((item) => ( + + ))} + {loopArrowFn(results.value)} + {loopForI(results.value)} + {loopForOf(results.value)} + {loopForIn(results.value)} + {loopWhile(results.value)} +
      + ); + }); +"# + .to_string(), + transpile_ts: true, + transpile_jsx: true, + ..TestInput::default() + }); +} + // TODO(misko): Make this test work by implementing strict serialization. // #[test] // fn example_of_synchronous_qrl_that_cant_be_serialized() { diff --git a/packages/qwik/src/optimizer/core/src/transform.rs b/packages/qwik/src/optimizer/core/src/transform.rs index c2332313411..2ac682e328e 100644 --- a/packages/qwik/src/optimizer/core/src/transform.rs +++ b/packages/qwik/src/optimizer/core/src/transform.rs @@ -577,12 +577,6 @@ impl<'a> QwikTransform<'a> { let (scoped_idents, is_const, has_const) = compute_scoped_idents(&descendent_idents, &decl_collect); - if !has_const { - // if the inputs to the expression don't have at least one constant (meaning something that could be a signal), - // turning it into `_fnSignal` is useless - return (None, is_const); - } - // simple variable expression, no need to inline if let ast::Expr::Ident(_) = folded { return (None, is_const); @@ -599,6 +593,12 @@ impl<'a> QwikTransform<'a> { } } + if !has_const { + // if the inputs to the expression don't have at least one constant (meaning something that could be a signal), + // turning it into `_fnSignal` is useless + return (None, is_const); + } + let serialize_fn = self.options.is_server; let inlined_fn = self.ensure_core_import(&_INLINED_FN); convert_inlined_fn( @@ -1377,18 +1377,19 @@ impl<'a> QwikTransform<'a> { key: node.key.clone(), }), )); - if is_fn { + if !is_const { + static_listeners = false; + } + + if is_fn || spread_props_count > 0 { if is_const { maybe_const_props .push(converted_prop.fold_with(self)); } else { var_props.push(converted_prop.fold_with(self)); } - } else if is_const { - event_handlers.push(converted_prop.fold_with(self)); } else { - static_listeners = false; - maybe_const_props.push(converted_prop.fold_with(self)); + event_handlers.push(converted_prop.fold_with(self)); } } else { let const_prop = is_const_expr( @@ -1792,10 +1793,7 @@ impl<'a> Fold for QwikTransform<'a> { for param in &node.params { let mut identifiers = vec![]; let is_identifier = collect_from_pat(¶m.pat, &mut identifiers); - let mut is_constant = false; - if is_qcomponent { - is_constant = is_identifier; - } + let is_constant = if is_qcomponent { is_identifier } else { false }; current_scope.extend( identifiers .into_iter() @@ -1828,10 +1826,7 @@ impl<'a> Fold for QwikTransform<'a> { for param in &node.params { let mut identifiers = vec![]; let is_identifier = collect_from_pat(param, &mut identifiers); - let mut is_constant = false; - if is_qcomponent { - is_constant = is_identifier; - } + let is_constant = if is_qcomponent { is_identifier } else { false }; current_scope.extend( identifiers .into_iter() From 5408c40cc360b71106a81cffbb1c6ac2d80083a1 Mon Sep 17 00:00:00 2001 From: Varixo Date: Mon, 23 Dec 2024 16:14:06 +0100 Subject: [PATCH 05/11] add changesets --- .changeset/afraid-bags-jam.md | 5 +++++ .changeset/shaggy-poems-return.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/afraid-bags-jam.md create mode 100644 .changeset/shaggy-poems-return.md diff --git a/.changeset/afraid-bags-jam.md b/.changeset/afraid-bags-jam.md new file mode 100644 index 00000000000..fc00f500181 --- /dev/null +++ b/.changeset/afraid-bags-jam.md @@ -0,0 +1,5 @@ +--- +'@qwik.dev/core': patch +--- + +fix: Resource without onPending callback diff --git a/.changeset/shaggy-poems-return.md b/.changeset/shaggy-poems-return.md new file mode 100644 index 00000000000..1d869f2f7ac --- /dev/null +++ b/.changeset/shaggy-poems-return.md @@ -0,0 +1,5 @@ +--- +'@qwik.dev/core': patch +--- + +fix: event handlers in loops From 81a2f7b367dc78943c39632c81bae429d40ecf58 Mon Sep 17 00:00:00 2001 From: Varixo Date: Sun, 29 Dec 2024 20:45:31 +0100 Subject: [PATCH 06/11] better use-resource comment --- packages/qwik/src/core/use/use-resource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/qwik/src/core/use/use-resource.ts b/packages/qwik/src/core/use/use-resource.ts index ccd194c3360..0790456dfd0 100644 --- a/packages/qwik/src/core/use/use-resource.ts +++ b/packages/qwik/src/core/use/use-resource.ts @@ -197,7 +197,7 @@ function getResourceValueAsPromise(props: ResourceProps): Promise resource._resolved) as T; if (resolvedValue !== undefined) { - // resolved, pending without onPending prop or rejected with onRejected prop + // resolved, pending without onPending prop or rejected without onRejected prop return Promise.resolve(resolvedValue).then(props.onResolved); } } From 834a4518c1a154a153b8c30922290823d498ff11 Mon Sep 17 00:00:00 2001 From: Varixo Date: Sun, 29 Dec 2024 20:46:24 +0100 Subject: [PATCH 07/11] fix: for of handlers as var prop --- ...nent_with_event_listeners_inside_loop.snap | 6 +- .../qwik/src/optimizer/core/src/transform.rs | 64 +++++++++++++------ 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_component_with_event_listeners_inside_loop.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_component_with_event_listeners_inside_loop.snap index 6fda7cbb190..49522de24cf 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_component_with_event_listeners_inside_loop.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_component_with_event_listeners_inside_loop.snap @@ -296,12 +296,12 @@ export const App_component_ckEPmXZlub0 = ()=>{ } function loopForOf(results) { const items = []; - for (const item of results)items.push(/*#__PURE__*/ _jsxSorted("span", null, { + for (const item of results)items.push(/*#__PURE__*/ _jsxSorted("span", { onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_App_component_loopForOf_span_onClick_AjlGKUbcCKY"), "App_component_loopForOf_span_onClick_AjlGKUbcCKY", [ cart, item ]) - }, item, 3, "u6_2")); + }, null, item, 2, "u6_2")); return items; } function loopForIn(results) { @@ -351,7 +351,7 @@ export const App_component_ckEPmXZlub0 = ()=>{ }; -Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;;;yCAE8B;IACxB,MAAM,OAAO,SAAmB,EAAE;IAClC,MAAM,UAAU,UAAU;QAAC;KAAM;IACjC,SAAS,YAAY,OAAiB;QACpC,OAAO,QAAQ,GAAG,CAAC,CAAC,qBAClB,WAAC;gBACC,QAAQ;;;;qBAIP;IAGP;IACA,SAAS,SAAS,OAAiB;QACjC,MAAM,QAAQ,EAAE;QAChB,IAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,MAAM,EAAE,IAClC,MAAM,IAAI,eACR,WAAC;YACC,QAAQ;;;;;iBAIP,OAAO,CAAC,EAAE;QAIjB,OAAO;IACT;IACA,SAAS,UAAU,OAAiB;QAClC,MAAM,QAAQ,EAAE;QAChB,KAAK,MAAM,QAAQ,QACjB,MAAM,IAAI,eACR,WAAC;YACC,QAAQ;;;;WAIP;QAIP,OAAO;IACT;IACA,SAAS,UAAU,OAAiB;QAClC,MAAM,QAAQ,EAAE;QAChB,IAAK,MAAM,OAAO,QAChB,MAAM,IAAI,eACR,WAAC;YACC,QAAQ;;;;;qCAIP,EAAO,IAAK;;;;QAInB,OAAO;IACT;IACA,SAAS,UAAU,OAAiB;QAClC,MAAM,QAAQ,EAAE;QAChB,IAAI,IAAI;QACR,MAAO,IAAI,QAAQ,MAAM,CAAE;YACzB,MAAM,IAAI,eACR,WAAC;gBACC,QAAQ;;;;;qBAIP,OAAO,CAAC,EAAE;YAGf;QACF;QACA,OAAO;IACT;IACA,qBACE,WAAC;QACE,QAAQ,KAAK,CAAC,GAAG,CAAC,CAAC,qBAClB,WAAC;gBAEC,QAAQ;;;;;gBADR,IAAG;eAKF;QAGJ,YAAY,QAAQ,KAAK;QACzB,SAAS,QAAQ,KAAK;QACtB,UAAU,QAAQ,KAAK;QACvB,UAAU,QAAQ,KAAK;QACvB,UAAU,QAAQ,KAAK;;AAG9B\"}") +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;;;yCAE8B;IACxB,MAAM,OAAO,SAAmB,EAAE;IAClC,MAAM,UAAU,UAAU;QAAC;KAAM;IACjC,SAAS,YAAY,OAAiB;QACpC,OAAO,QAAQ,GAAG,CAAC,CAAC,qBAClB,WAAC;gBACC,QAAQ;;;;qBAIP;IAGP;IACA,SAAS,SAAS,OAAiB;QACjC,MAAM,QAAQ,EAAE;QAChB,IAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,MAAM,EAAE,IAClC,MAAM,IAAI,eACR,WAAC;YACC,QAAQ;;;;;iBAIP,OAAO,CAAC,EAAE;QAIjB,OAAO;IACT;IACA,SAAS,UAAU,OAAiB;QAClC,MAAM,QAAQ,EAAE;QAChB,KAAK,MAAM,QAAQ,QACjB,MAAM,IAAI,eACR,WAAC;YACC,QAAQ;;;;iBAIP;QAIP,OAAO;IACT;IACA,SAAS,UAAU,OAAiB;QAClC,MAAM,QAAQ,EAAE;QAChB,IAAK,MAAM,OAAO,QAChB,MAAM,IAAI,eACR,WAAC;YACC,QAAQ;;;;;qCAIP,EAAO,IAAK;;;;QAInB,OAAO;IACT;IACA,SAAS,UAAU,OAAiB;QAClC,MAAM,QAAQ,EAAE;QAChB,IAAI,IAAI;QACR,MAAO,IAAI,QAAQ,MAAM,CAAE;YACzB,MAAM,IAAI,eACR,WAAC;gBACC,QAAQ;;;;;qBAIP,OAAO,CAAC,EAAE;YAGf;QACF;QACA,OAAO;IACT;IACA,qBACE,WAAC;QACE,QAAQ,KAAK,CAAC,GAAG,CAAC,CAAC,qBAClB,WAAC;gBAEC,QAAQ;;;;;gBADR,IAAG;eAKF;QAGJ,YAAY,QAAQ,KAAK;QACzB,SAAS,QAAQ,KAAK;QACtB,UAAU,QAAQ,KAAK;QACvB,UAAU,QAAQ,KAAK;QACvB,UAAU,QAAQ,KAAK;;AAG9B\"}") /* { "origin": "test.tsx", diff --git a/packages/qwik/src/optimizer/core/src/transform.rs b/packages/qwik/src/optimizer/core/src/transform.rs index 2ac682e328e..8df10cb0156 100644 --- a/packages/qwik/src/optimizer/core/src/transform.rs +++ b/packages/qwik/src/optimizer/core/src/transform.rs @@ -1788,17 +1788,10 @@ impl<'a> Fold for QwikTransform<'a> { .last_mut() .expect("Declaration stack empty!"); - let is_qcomponent = self.stack_ctxt.last() == Some(&QCOMPONENT.to_string()); + let is_qcomponent = is_qcomponent(&self.stack_ctxt.last()); for param in &node.params { - let mut identifiers = vec![]; - let is_identifier = collect_from_pat(¶m.pat, &mut identifiers); - let is_constant = if is_qcomponent { is_identifier } else { false }; - current_scope.extend( - identifiers - .into_iter() - .map(|(id, _)| (id, IdentType::Var(is_constant))), - ); + current_scope.extend(process_node_props(¶m.pat, is_qcomponent)); } let o = node.fold_children_with(self); self.root_jsx_mode = prev; @@ -1821,17 +1814,10 @@ impl<'a> Fold for QwikTransform<'a> { .last_mut() .expect("Declaration stack empty!"); - let is_qcomponent = self.stack_ctxt.last() == Some(&QCOMPONENT.to_string()); + let is_qcomponent = is_qcomponent(&self.stack_ctxt.last()); for param in &node.params { - let mut identifiers = vec![]; - let is_identifier = collect_from_pat(param, &mut identifiers); - let is_constant = if is_qcomponent { is_identifier } else { false }; - current_scope.extend( - identifiers - .into_iter() - .map(|(id, _)| (id, IdentType::Var(is_constant))), - ); + current_scope.extend(process_node_props(param, is_qcomponent)); } let o = node.fold_children_with(self); @@ -1868,6 +1854,30 @@ impl<'a> Fold for QwikTransform<'a> { self.decl_stack.push(vec![]); let prev = self.root_jsx_mode; self.root_jsx_mode = true; + + let current_scope = self + .decl_stack + .last_mut() + .expect("Declaration stack empty!"); + + let is_qcomponent = is_qcomponent(&self.stack_ctxt.last()); + + match node.left.clone() { + ast::ForHead::VarDecl(var_decl) => { + for decl in &var_decl.decls { + current_scope.extend(process_node_props(&decl.name, is_qcomponent)); + } + } + ast::ForHead::UsingDecl(using_decl) => { + for decl in &using_decl.decls { + current_scope.extend(process_node_props(&decl.name, is_qcomponent)); + } + } + ast::ForHead::Pat(pat) => { + current_scope.extend(process_node_props(&pat, is_qcomponent)); + } + } + let o = node.fold_children_with(self); self.root_jsx_mode = prev; self.decl_stack.pop(); @@ -2361,3 +2371,21 @@ fn is_text_only(node: &str) -> bool { "text" | "textarea" | "title" | "option" | "script" | "style" | "noscript" ) } + +fn is_qcomponent(stack_item: &Option<&String>) -> bool { + *stack_item == Some(&QCOMPONENT.to_string()) +} + +fn process_node_props(pat: &ast::Pat, is_qcomponent: bool) -> Vec { + let mut identifiers = vec![]; + let mut processed_scope_data: Vec = vec![]; + let is_identifier = collect_from_pat(pat, &mut identifiers); + let is_constant = if is_qcomponent { is_identifier } else { false }; + processed_scope_data.extend( + identifiers + .into_iter() + .map(|(id, _)| (id, IdentType::Var(is_constant))), + ); + + processed_scope_data +} From da226e5fe57a3549cfa131072350ebde823243f7 Mon Sep 17 00:00:00 2001 From: Varixo Date: Sun, 29 Dec 2024 20:52:03 +0100 Subject: [PATCH 08/11] add test for moving has_const condition --- ...ould_wrap_inner_inline_component_prop.snap | 87 +++++++++++++++++++ packages/qwik/src/optimizer/core/src/test.rs | 30 +++++++ 2 files changed, 117 insertions(+) create mode 100644 packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_wrap_inner_inline_component_prop.snap diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_wrap_inner_inline_component_prop.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_wrap_inner_inline_component_prop.snap new file mode 100644 index 00000000000..2a16721bb26 --- /dev/null +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__should_wrap_inner_inline_component_prop.snap @@ -0,0 +1,87 @@ +--- +source: packages/qwik/src/optimizer/core/src/test.rs +assertion_line: 3959 +expression: output +snapshot_kind: text +--- +==INPUT== + + +import { $, component$, useStore, useSignal } from '@qwik.dev/core'; +export default component$((props: { id: number }) => { + const renders = useStore( + { + count: 0, + }, + { reactive: false } + ); + renders.count++; + const rerenders = renders.count + 0; + const Id = (props: any) =>
      Id: {props.id}
      ; + return ( + <> + + {rerenders} + + ); + }); + +============================= test.js == + +import { componentQrl } from "@qwik.dev/core"; +import { qrl } from "@qwik.dev/core"; +export default /*#__PURE__*/ componentQrl(/*#__PURE__*/ qrl(()=>import("./test.tsx_test_component_LUXeXe0DQrg"), "test_component_LUXeXe0DQrg")); + + +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;AAEA,6BAAe,mHAgBR\"}") +============================= test.tsx_test_component_LUXeXe0DQrg.js (ENTRY POINT)== + +import { Fragment as _Fragment } from "@qwik.dev/core/jsx-runtime"; +import { _jsxSorted } from "@qwik.dev/core"; +import { _wrapProp } from "@qwik.dev/core"; +import { useStore } from "@qwik.dev/core"; +export const test_component_LUXeXe0DQrg = (props)=>{ + const renders = useStore({ + count: 0 + }, { + reactive: false + }); + renders.count++; + const rerenders = renders.count + 0; + const Id = (props)=>/*#__PURE__*/ _jsxSorted("div", null, null, [ + "Id: ", + _wrapProp(props, "id") + ], 1, "u6_0"); + return /*#__PURE__*/ _jsxSorted(_Fragment, null, null, [ + /*#__PURE__*/ _jsxSorted(Id, null, { + id: _wrapProp(props, "id") + }, null, 3, "u6_1"), + rerenders + ], 1, "u6_2"); +}; + + +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;;0CAE0B,CAAC;IACrB,MAAM,UAAU,SACd;QACE,OAAO;IACT,GACA;QAAE,UAAU;IAAM;IAEpB,QAAQ,KAAK;IACb,MAAM,YAAY,QAAQ,KAAK,GAAG;IAClC,MAAM,KAAK,CAAC,sBAAe,WAAC;YAAI;sBAAK;;IACrC,qBACE;sBACE,WAAC;YAAG,EAAE,YAAE;;QACP;;AAGP\"}") +/* +{ + "origin": "test.tsx", + "name": "test_component_LUXeXe0DQrg", + "entry": null, + "displayName": "test.tsx_test_component", + "hash": "LUXeXe0DQrg", + "canonicalFilename": "test.tsx_test_component_LUXeXe0DQrg", + "path": "", + "extension": "js", + "parent": null, + "ctxKind": "function", + "ctxName": "component$", + "captures": false, + "loc": [ + 97, + 467 + ] +} +*/ +== DIAGNOSTICS == + +[] diff --git a/packages/qwik/src/optimizer/core/src/test.rs b/packages/qwik/src/optimizer/core/src/test.rs index d4aa2e94172..aac46778fcf 100644 --- a/packages/qwik/src/optimizer/core/src/test.rs +++ b/packages/qwik/src/optimizer/core/src/test.rs @@ -3954,6 +3954,36 @@ export const App = component$(() => { }); } +#[test] +fn should_wrap_inner_inline_component_prop() { + test_input!(TestInput { + code: r#" +import { $, component$, useStore, useSignal } from '@qwik.dev/core'; +export default component$((props: { id: number }) => { + const renders = useStore( + { + count: 0, + }, + { reactive: false } + ); + renders.count++; + const rerenders = renders.count + 0; + const Id = (props: any) =>
      Id: {props.id}
      ; + return ( + <> + + {rerenders} + + ); + }); +"# + .to_string(), + transpile_ts: true, + transpile_jsx: true, + ..TestInput::default() + }); +} + // TODO(misko): Make this test work by implementing strict serialization. // #[test] // fn example_of_synchronous_qrl_that_cant_be_serialized() { From e26a5253e81a1f5fe2f59e08e6f6d0419779d7af Mon Sep 17 00:00:00 2001 From: Varixo Date: Sun, 29 Dec 2024 20:59:04 +0100 Subject: [PATCH 09/11] fix snapshots and tests after merge --- packages/qwik/src/core/tests/use-store.spec.tsx | 8 ++++---- ...est__destructure_args_inline_cmp_block_stmt.snap | 11 ++++------- ...st__destructure_args_inline_cmp_block_stmt2.snap | 11 ++++------- ...test__destructure_args_inline_cmp_expr_stmt.snap | 13 +++++-------- 4 files changed, 17 insertions(+), 26 deletions(-) diff --git a/packages/qwik/src/core/tests/use-store.spec.tsx b/packages/qwik/src/core/tests/use-store.spec.tsx index 56964334a64..769ac7aa581 100644 --- a/packages/qwik/src/core/tests/use-store.spec.tsx +++ b/packages/qwik/src/core/tests/use-store.spec.tsx @@ -394,8 +394,8 @@ describe.each([ expect(vNode).toMatchVDOM( - - + + @@ -406,8 +406,8 @@ describe.each([ expect(vNode).toMatchVDOM( - - + + diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt.snap index 4278bce1016..21a3a09d74e 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt.snap @@ -20,22 +20,19 @@ snapshot_kind: text ============================= test.js == -import { _fnSignal } from "@qwik.dev/core"; import { qrl } from "@qwik.dev/core"; import { _jsxSorted } from "@qwik.dev/core"; export default ((props)=>{ - return /*#__PURE__*/ _jsxSorted("div", null, { - "data-is-active": _fnSignal((p0)=>p0.data.selectedOutputDetail === 'options', [ - props - ], 'p0.data.selectedOutputDetail==="options"'), + return /*#__PURE__*/ _jsxSorted("div", { + "data-is-active": props.data.selectedOutputDetail === 'options', onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_test_div_onClick_GbMO6TGQv9M"), "test_div_onClick_GbMO6TGQv9M", [ props ]) - }, null, 3, "u6_0"); + }, null, null, 2, "u6_0"); }); -Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;AACE,eAAe,CAAA;IACP,qBACE,WAAC;QACC,gBAAc,kBAAE,GAHV,KAGe,oBAAoB,KAAK;;;QAC9C,QAAQ;;;;AAKpB,CAAA,EAAE\"}") +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;AACE,eAAe,CAAA;IACP,qBACE,WAAC;QACC,kBAAgB,MAHV,KAGe,oBAAoB,KAAK;QAC9C,QAAQ;;;;AAKpB,CAAA,EAAE\"}") ============================= test.tsx_test_div_onClick_GbMO6TGQv9M.js (ENTRY POINT)== import { useLexicalScope } from "@qwik.dev/core"; diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt2.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt2.snap index 41a2b9694c0..e0765d771be 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt2.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt2.snap @@ -21,22 +21,19 @@ snapshot_kind: text ============================= test.js == -import { _fnSignal } from "@qwik.dev/core"; import { qrl } from "@qwik.dev/core"; import { _jsxSorted } from "@qwik.dev/core"; export default ((props)=>{ - return /*#__PURE__*/ _jsxSorted("div", null, { - "data-is-active": _fnSignal((p0)=>p0.data.selectedOutputDetail === 'options', [ - props - ], 'p0.data.selectedOutputDetail==="options"'), + return /*#__PURE__*/ _jsxSorted("div", { + "data-is-active": props.data.selectedOutputDetail === 'options', onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_test_div_onClick_GbMO6TGQv9M"), "test_div_onClick_GbMO6TGQv9M", [ props ]) - }, null, 3, "u6_0"); + }, null, null, 2, "u6_0"); }); -Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;AACE,eAAe,CAAA,CAAC;IAER,qBACE,WAAC;QACC,gBAAc,kBAAE,GAHlB,KAGuB,oBAAoB,KAAK;;;QAC9C,QAAQ;;;;AAKpB,CAAA,EAAE\"}") +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;AACE,eAAe,CAAA,CAAC;IAER,qBACE,WAAC;QACC,kBAAgB,AAHT,MAAT,KAGuB,oBAAoB,KAAK;QAC9C,QAAQ;;;;AAKpB,CAAA,EAAE\"}") ============================= test.tsx_test_div_onClick_GbMO6TGQv9M.js (ENTRY POINT)== import { useLexicalScope } from "@qwik.dev/core"; diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_expr_stmt.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_expr_stmt.snap index 16ff367f5f5..cea52472c09 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_expr_stmt.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_expr_stmt.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 3673 +assertion_line: 3696 expression: output snapshot_kind: text --- @@ -17,20 +17,17 @@ snapshot_kind: text ============================= test.js == -import { _fnSignal } from "@qwik.dev/core"; import { qrl } from "@qwik.dev/core"; import { _jsxSorted } from "@qwik.dev/core"; -export default ((props)=>/*#__PURE__*/ _jsxSorted("div", null, { - "data-is-active": _fnSignal((p0)=>p0.data.selectedOutputDetail === 'options', [ - props - ], 'p0.data.selectedOutputDetail==="options"'), +export default ((props)=>/*#__PURE__*/ _jsxSorted("div", { + "data-is-active": props.data.selectedOutputDetail === 'options', onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_test_div_onClick_GbMO6TGQv9M"), "test_div_onClick_GbMO6TGQv9M", [ props ]) - }, null, 3, "u6_0")); + }, null, null, 2, "u6_0")); -Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;AACE,eAAe,CAAA,uBACL,WAAC;QACC,gBAAc,kBAAE,GAFV,KAEe,oBAAoB,KAAK;;;QAC9C,QAAQ;;;uBAGT,EAAE\"}") +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;AACE,eAAe,CAAA,uBACL,WAAC;QACC,kBAAgB,MAFV,KAEe,oBAAoB,KAAK;QAC9C,QAAQ;;;6BAGT,EAAE\"}") ============================= test.tsx_test_div_onClick_GbMO6TGQv9M.js (ENTRY POINT)== import { useLexicalScope } from "@qwik.dev/core"; From 90a6ede8b78c8c512d6221bcd9c9787824d40760 Mon Sep 17 00:00:00 2001 From: Varixo Date: Thu, 2 Jan 2025 09:37:35 +0100 Subject: [PATCH 10/11] fix: top level inline components props --- ...estructure_args_inline_cmp_block_stmt.snap | 11 ++++++---- ...structure_args_inline_cmp_block_stmt2.snap | 11 ++++++---- ...destructure_args_inline_cmp_expr_stmt.snap | 11 ++++++---- .../qwik/src/optimizer/core/src/transform.rs | 20 ++++++++++++++----- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt.snap index 21a3a09d74e..4278bce1016 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt.snap @@ -20,19 +20,22 @@ snapshot_kind: text ============================= test.js == +import { _fnSignal } from "@qwik.dev/core"; import { qrl } from "@qwik.dev/core"; import { _jsxSorted } from "@qwik.dev/core"; export default ((props)=>{ - return /*#__PURE__*/ _jsxSorted("div", { - "data-is-active": props.data.selectedOutputDetail === 'options', + return /*#__PURE__*/ _jsxSorted("div", null, { + "data-is-active": _fnSignal((p0)=>p0.data.selectedOutputDetail === 'options', [ + props + ], 'p0.data.selectedOutputDetail==="options"'), onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_test_div_onClick_GbMO6TGQv9M"), "test_div_onClick_GbMO6TGQv9M", [ props ]) - }, null, null, 2, "u6_0"); + }, null, 3, "u6_0"); }); -Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;AACE,eAAe,CAAA;IACP,qBACE,WAAC;QACC,kBAAgB,MAHV,KAGe,oBAAoB,KAAK;QAC9C,QAAQ;;;;AAKpB,CAAA,EAAE\"}") +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;AACE,eAAe,CAAA;IACP,qBACE,WAAC;QACC,gBAAc,kBAAE,GAHV,KAGe,oBAAoB,KAAK;;;QAC9C,QAAQ;;;;AAKpB,CAAA,EAAE\"}") ============================= test.tsx_test_div_onClick_GbMO6TGQv9M.js (ENTRY POINT)== import { useLexicalScope } from "@qwik.dev/core"; diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt2.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt2.snap index e0765d771be..41a2b9694c0 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt2.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_block_stmt2.snap @@ -21,19 +21,22 @@ snapshot_kind: text ============================= test.js == +import { _fnSignal } from "@qwik.dev/core"; import { qrl } from "@qwik.dev/core"; import { _jsxSorted } from "@qwik.dev/core"; export default ((props)=>{ - return /*#__PURE__*/ _jsxSorted("div", { - "data-is-active": props.data.selectedOutputDetail === 'options', + return /*#__PURE__*/ _jsxSorted("div", null, { + "data-is-active": _fnSignal((p0)=>p0.data.selectedOutputDetail === 'options', [ + props + ], 'p0.data.selectedOutputDetail==="options"'), onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_test_div_onClick_GbMO6TGQv9M"), "test_div_onClick_GbMO6TGQv9M", [ props ]) - }, null, null, 2, "u6_0"); + }, null, 3, "u6_0"); }); -Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;AACE,eAAe,CAAA,CAAC;IAER,qBACE,WAAC;QACC,kBAAgB,AAHT,MAAT,KAGuB,oBAAoB,KAAK;QAC9C,QAAQ;;;;AAKpB,CAAA,EAAE\"}") +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;AACE,eAAe,CAAA,CAAC;IAER,qBACE,WAAC;QACC,gBAAc,kBAAE,GAHlB,KAGuB,oBAAoB,KAAK;;;QAC9C,QAAQ;;;;AAKpB,CAAA,EAAE\"}") ============================= test.tsx_test_div_onClick_GbMO6TGQv9M.js (ENTRY POINT)== import { useLexicalScope } from "@qwik.dev/core"; diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_expr_stmt.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_expr_stmt.snap index cea52472c09..3edac5032ea 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_expr_stmt.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__destructure_args_inline_cmp_expr_stmt.snap @@ -17,17 +17,20 @@ snapshot_kind: text ============================= test.js == +import { _fnSignal } from "@qwik.dev/core"; import { qrl } from "@qwik.dev/core"; import { _jsxSorted } from "@qwik.dev/core"; -export default ((props)=>/*#__PURE__*/ _jsxSorted("div", { - "data-is-active": props.data.selectedOutputDetail === 'options', +export default ((props)=>/*#__PURE__*/ _jsxSorted("div", null, { + "data-is-active": _fnSignal((p0)=>p0.data.selectedOutputDetail === 'options', [ + props + ], 'p0.data.selectedOutputDetail==="options"'), onClick$: /*#__PURE__*/ qrl(()=>import("./test.tsx_test_div_onClick_GbMO6TGQv9M"), "test_div_onClick_GbMO6TGQv9M", [ props ]) - }, null, null, 2, "u6_0")); + }, null, 3, "u6_0")); -Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;AACE,eAAe,CAAA,uBACL,WAAC;QACC,kBAAgB,MAFV,KAEe,oBAAoB,KAAK;QAC9C,QAAQ;;;6BAGT,EAAE\"}") +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;AACE,eAAe,CAAA,uBACL,WAAC;QACC,gBAAc,kBAAE,GAFV,KAEe,oBAAoB,KAAK;;;QAC9C,QAAQ;;;uBAGT,EAAE\"}") ============================= test.tsx_test_div_onClick_GbMO6TGQv9M.js (ENTRY POINT)== import { useLexicalScope } from "@qwik.dev/core"; diff --git a/packages/qwik/src/optimizer/core/src/transform.rs b/packages/qwik/src/optimizer/core/src/transform.rs index 8df10cb0156..d692a781db7 100644 --- a/packages/qwik/src/optimizer/core/src/transform.rs +++ b/packages/qwik/src/optimizer/core/src/transform.rs @@ -1789,9 +1789,11 @@ impl<'a> Fold for QwikTransform<'a> { .expect("Declaration stack empty!"); let is_qcomponent = is_qcomponent(&self.stack_ctxt.last()); + let is_top_level_inline_component = is_top_level(&self.stack_ctxt); + let is_component = is_qcomponent || is_top_level_inline_component; for param in &node.params { - current_scope.extend(process_node_props(¶m.pat, is_qcomponent)); + current_scope.extend(process_node_props(¶m.pat, is_component)); } let o = node.fold_children_with(self); self.root_jsx_mode = prev; @@ -1815,9 +1817,11 @@ impl<'a> Fold for QwikTransform<'a> { .expect("Declaration stack empty!"); let is_qcomponent = is_qcomponent(&self.stack_ctxt.last()); + let is_top_level_inline_component = is_top_level(&self.stack_ctxt); + let is_component = is_qcomponent || is_top_level_inline_component; for param in &node.params { - current_scope.extend(process_node_props(param, is_qcomponent)); + current_scope.extend(process_node_props(param, is_component)); } let o = node.fold_children_with(self); @@ -1861,20 +1865,22 @@ impl<'a> Fold for QwikTransform<'a> { .expect("Declaration stack empty!"); let is_qcomponent = is_qcomponent(&self.stack_ctxt.last()); + let is_top_level_inline_component = is_top_level(&self.stack_ctxt); + let is_component = is_qcomponent || is_top_level_inline_component; match node.left.clone() { ast::ForHead::VarDecl(var_decl) => { for decl in &var_decl.decls { - current_scope.extend(process_node_props(&decl.name, is_qcomponent)); + current_scope.extend(process_node_props(&decl.name, is_component)); } } ast::ForHead::UsingDecl(using_decl) => { for decl in &using_decl.decls { - current_scope.extend(process_node_props(&decl.name, is_qcomponent)); + current_scope.extend(process_node_props(&decl.name, is_component)); } } ast::ForHead::Pat(pat) => { - current_scope.extend(process_node_props(&pat, is_qcomponent)); + current_scope.extend(process_node_props(&pat, is_component)); } } @@ -2376,6 +2382,10 @@ fn is_qcomponent(stack_item: &Option<&String>) -> bool { *stack_item == Some(&QCOMPONENT.to_string()) } +const fn is_top_level(stack: &[String]) -> bool { + stack.len() == 1 +} + fn process_node_props(pat: &ast::Pat, is_qcomponent: bool) -> Vec { let mut identifiers = vec![]; let mut processed_scope_data: Vec = vec![]; From 997b6722ba2c4073ab511a4297ad4ffe960c51ad Mon Sep 17 00:00:00 2001 From: Varixo Date: Thu, 2 Jan 2025 09:59:06 +0100 Subject: [PATCH 11/11] extend use-lexical-scope test --- .../src/core/tests/use-lexical-scope.spec.tsx | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/qwik/src/core/tests/use-lexical-scope.spec.tsx b/packages/qwik/src/core/tests/use-lexical-scope.spec.tsx index 42776c77b73..935dd5fddf2 100644 --- a/packages/qwik/src/core/tests/use-lexical-scope.spec.tsx +++ b/packages/qwik/src/core/tests/use-lexical-scope.spec.tsx @@ -20,15 +20,15 @@ describe.each([ const Parent = component$(() => { const cart = useStore([]); - const results = useSignal(['foo']); + const results = useSignal(['foo', 'bar']); return (
      - + - {results.value.map((item) => ( + {results.value.map((item, key) => ( - + +
        @@ -65,22 +66,24 @@ describe.each([
        - + +
          ); - await trigger(document.body, 'button#second', 'click'); + await trigger(document.body, 'button#second-1', 'click'); expect(vNode).toMatchVDOM(
          - + +
          • - item + item2