Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tidy-defaults-track.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@qwik.dev/optimizer': patch
---

fix: track props initialized with function call defaults
107 changes: 98 additions & 9 deletions packages/optimizer/core/src/props_destructuring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use swc_atoms::Atom;
use swc_common::DUMMY_SP;
use swc_ecmascript::ast;
use swc_ecmascript::utils::private_ident;
use swc_ecmascript::visit::{VisitMut, VisitMutWith};
use swc_ecmascript::visit::{Visit, VisitMut, VisitMutWith, VisitWith};

struct PropsDestructuring<'a> {
component_ident: Option<Id>,
Expand Down Expand Up @@ -82,8 +82,8 @@ impl<'a> PropsDestructuring<'a> {
}

let new_ident = private_ident!("_rawProps");
if let Some((rest_id_opt, local)) =
transform_pat(ast::Expr::Ident(new_ident.clone()), obj, self)
if let Some((rest_id_opt, local, default_stmts)) =
transform_pat(ast::Expr::Ident(new_ident.clone()), obj, self, true)
{
if let Some(rest_id) = rest_id_opt {
let omit_fn = self.global_collect.import(&_REST_PROPS, self.core_module);
Expand All @@ -100,6 +100,7 @@ impl<'a> PropsDestructuring<'a> {
self.identifiers.insert(id, expr);
}
arrow.params[0] = ast::Pat::Ident(ast::BindingIdent::from(new_ident));
prepend_stmts(arrow, default_stmts);
}
}
if let ast::BlockStmtOrExpr::BlockStmt(body) = &mut *arrow.body {
Expand Down Expand Up @@ -240,8 +241,8 @@ impl<'a> PropsDestructuring<'a> {
}
}
ast::Pat::Object(obj_pat) => {
if let Some((rest_id, local)) =
transform_pat(new_ref.clone(), obj_pat, self)
if let Some((rest_id, local, _)) =
transform_pat(new_ref.clone(), obj_pat, self, false)
{
if let Some(rest_id) = rest_id {
let omit_fn = self
Expand Down Expand Up @@ -377,15 +378,18 @@ impl<'a> VisitMut for PropsDestructuring<'a> {
}
}

type TransformPatReturn = (Option<Id>, Vec<(Id, Atom, ast::Expr)>);
type TransformPatReturn = (Option<Id>, Vec<(Id, Atom, ast::Expr)>, Vec<ast::Stmt>);
fn transform_pat(
new_ident: ast::Expr,
obj: &ast::ObjectPat,
props_transform: &mut PropsDestructuring,
allow_dynamic_defaults: bool,
) -> Option<TransformPatReturn> {
let mut local = vec![];
let mut default_stmts = vec![];
let mut skip = false;
let mut rest_id = None;
let destructured_names = collect_destructured_names(obj);
for prop in &obj.props {
match prop {
ast::ObjectPatProp::Assign(ref v) => {
Expand All @@ -406,6 +410,13 @@ fn transform_pat(
right: value.clone(),
}),
));
} else if allow_dynamic_defaults
&& !references_destructured_name(value, &destructured_names)
{
let (default_expr, default_stmt) =
create_dynamic_default(access, value.clone());
local.push((id!(v.key), v.key.sym.clone(), default_expr));
default_stmts.push(default_stmt);
} else {
skip = true;
}
Expand Down Expand Up @@ -469,6 +480,18 @@ fn transform_pat(
right: value.clone(),
}),
));
} else if allow_dynamic_defaults
&& !references_destructured_name(value, &destructured_names)
{
let access = ast::Expr::Member(ast::MemberExpr {
obj: Box::new(new_ident.clone()),
prop,
span: DUMMY_SP,
});
let (default_expr, default_stmt) =
create_dynamic_default(access, value.clone());
local.push((id!(ident.id), key_atom.clone(), default_expr));
default_stmts.push(default_stmt);
} else {
skip = true;
}
Expand All @@ -494,7 +517,65 @@ fn transform_pat(
return None;
}
// Allow case with only rest binding (no local fields)
Some((rest_id, local))
Some((rest_id, local, default_stmts))
}

fn create_dynamic_default(access: ast::Expr, value: Box<ast::Expr>) -> (ast::Expr, ast::Stmt) {
let default_ident = private_ident!("_defaultValue");
let default_stmt = ast::Stmt::Decl(ast::Decl::Var(Box::new(ast::VarDecl {
kind: ast::VarDeclKind::Const,
decls: vec![ast::VarDeclarator {
definite: false,
span: DUMMY_SP,
init: Some(Box::new(ast::Expr::Cond(ast::CondExpr {
span: DUMMY_SP,
test: Box::new(is_undefined(access.clone())),
cons: value,
alt: ast::Expr::undefined(DUMMY_SP),
}))),
name: ast::Pat::Ident(ast::BindingIdent::from(default_ident.clone())),
}],
..Default::default()
})));
let default_expr = ast::Expr::Cond(ast::CondExpr {
span: DUMMY_SP,
test: Box::new(is_undefined(access.clone())),
cons: Box::new(ast::Expr::Ident(default_ident)),
alt: Box::new(access),
});

(default_expr, default_stmt)
}

fn is_undefined(expr: ast::Expr) -> ast::Expr {
ast::Expr::Bin(ast::BinExpr {
span: DUMMY_SP,
op: ast::BinaryOp::EqEqEq,
left: Box::new(expr),
right: ast::Expr::undefined(DUMMY_SP),
})
}

fn references_destructured_name(expr: &ast::Expr, names: &HashSet<Id>) -> bool {
struct Finder<'a> {
names: &'a HashSet<Id>,
found: bool,
}

impl Visit for Finder<'_> {
fn visit_ident(&mut self, ident: &ast::Ident) {
if self.names.contains(&id!(ident)) {
self.found = true;
}
}
}

let mut finder = Finder {
names,
found: false,
};
expr.visit_with(&mut finder);
finder.found
}

fn transform_rest(
Expand Down Expand Up @@ -528,13 +609,21 @@ fn transform_rest(
} else {
create_omit_props(omit_fn, rest_id, props_expr, omit)
};
prepend_stmts(arrow, vec![new_stmt]);
}

fn prepend_stmts(arrow: &mut ast::ArrowExpr, mut stmts: Vec<ast::Stmt>) {
if stmts.is_empty() {
return;
}
match &mut arrow.body {
box ast::BlockStmtOrExpr::BlockStmt(block) => {
block.stmts.insert(0, new_stmt);
block.stmts.splice(0..0, stmts);
}
box ast::BlockStmtOrExpr::Expr(ref expr) => {
stmts.push(create_return_stmt(expr.clone()));
arrow.body = Box::new(ast::BlockStmtOrExpr::BlockStmt(ast::BlockStmt {
stmts: vec![new_stmt, create_return_stmt(expr.clone())],
stmts,
..Default::default()
}));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ expression: output

import { $, component$, useTask$ } from '@qwik.dev/core';
import { CONST } from 'const';
const getValue = () => 0;
export const Works = component$(({
count,
some = 1+2,
Expand Down Expand Up @@ -36,17 +37,22 @@ export const NoWorks2 = component$(({count, stuff: {hey}}) => {
);
});

export const NoWorks3 = component$(({count, stuff = hola()}) => {
export const DynamicDefaults = component$(({count, stuff = getValue(), other: value = getValue()}) => {
console.log(stuff);
useTask$(({track}) => {
track(() => count);
console.log(count);
track(() => stuff);
track(() => value);
console.log(count, stuff, value);
});
return (
<div class={count}>{count}</div>
<div class={stuff}>{value}</div>
);
});

export const ReferencedDefault = component$(({first = getValue(second), second}) => (
<div>{first}{second}</div>
));

============================= test.js ==

import { _restProps } from "@qwik.dev/core";
Expand All @@ -67,11 +73,16 @@ const _hf1 = (p0)=>({
some: p0.some ?? 3
});
const _hf1_str = "{some:p0.some??1+2}";
const _hf2 = (p0, p1)=>p1.stuff === void 0 ? p0 : p1.stuff;
const _hf2_str = "p1.stuff===void 0?p0:p1.stuff";
const _hf3 = (p0, p1)=>p1.other === void 0 ? p0 : p1.other;
const _hf3_str = "p1.other===void 0?p0:p1.other";
//
const q_DynamicDefaults_component_nZOHO1uiqf4 = /*#__PURE__*/ _noopQrl("DynamicDefaults_component_nZOHO1uiqf4");
const q_DynamicDefaults_component_useTask_Od2wjq7X07c = /*#__PURE__*/ _noopQrl("DynamicDefaults_component_useTask_Od2wjq7X07c");
const q_NoWorks2_component_JPD9t2HyEKg = /*#__PURE__*/ _noopQrl("NoWorks2_component_JPD9t2HyEKg");
const q_NoWorks2_component_useTask_lXiqwbxxjq0 = /*#__PURE__*/ _noopQrl("NoWorks2_component_useTask_lXiqwbxxjq0");
const q_NoWorks3_component_fc13h5yYn14 = /*#__PURE__*/ _noopQrl("NoWorks3_component_fc13h5yYn14");
const q_NoWorks3_component_useTask_3cQGU0s1VwU = /*#__PURE__*/ _noopQrl("NoWorks3_component_useTask_3cQGU0s1VwU");
const q_ReferencedDefault_component_xTfXU3xq5Ow = /*#__PURE__*/ _noopQrl("ReferencedDefault_component_xTfXU3xq5Ow");
const q_Works_component_t45qL4vNGv0 = /*#__PURE__*/ _noopQrl("Works_component_t45qL4vNGv0");
const q_Works_component_useTask_pjo5U5Ikll0 = /*#__PURE__*/ _noopQrl("Works_component_useTask_pjo5U5Ikll0");
//
Expand Down Expand Up @@ -121,26 +132,44 @@ q_NoWorks2_component_JPD9t2HyEKg.s(({ count, stuff: { hey } })=>{
class: count
}, null, count, 1, "u6_1");
});
q_NoWorks3_component_useTask_3cQGU0s1VwU.s(({ track })=>{
const count = _captures[0];
track(()=>count);
console.log(count);
q_DynamicDefaults_component_useTask_Od2wjq7X07c.s(({ track })=>{
const _defaultValue = _captures[0], _defaultValue1 = _captures[1], _rawProps = _captures[2];
track(()=>_rawProps.stuff === void 0 ? _defaultValue : _rawProps.stuff);
track(()=>_rawProps.other === void 0 ? _defaultValue1 : _rawProps.other);
console.log(_rawProps.count, _rawProps.stuff === void 0 ? _defaultValue : _rawProps.stuff, _rawProps.other === void 0 ? _defaultValue1 : _rawProps.other);
});
q_NoWorks3_component_fc13h5yYn14.s(({ count, stuff = hola() })=>{
console.log(stuff);
useTaskQrl(q_NoWorks3_component_useTask_3cQGU0s1VwU.w([
count
const getValue = ()=>0;
q_DynamicDefaults_component_nZOHO1uiqf4.s((_rawProps)=>{
const _defaultValue = _rawProps.stuff === void 0 ? getValue() : void 0;
const _defaultValue1 = _rawProps.other === void 0 ? getValue() : void 0;
console.log(_rawProps.stuff === void 0 ? _defaultValue : _rawProps.stuff);
useTaskQrl(q_DynamicDefaults_component_useTask_Od2wjq7X07c.w([
_defaultValue,
_defaultValue1,
_rawProps
]));
return /*#__PURE__*/ _jsxSorted("div", {
class: count
}, null, count, 1, "u6_2");
class: _fnSignal(_hf2, [
_defaultValue,
_rawProps
], _hf2_str)
}, null, _fnSignal(_hf3, [
_defaultValue1,
_rawProps
], _hf3_str), 1, "u6_2");
});
q_ReferencedDefault_component_xTfXU3xq5Ow.s(({ first = getValue(second), second })=>/*#__PURE__*/ _jsxSorted("div", null, null, [
first,
second
], 1, "u6_3"));
export const Works = /*#__PURE__*/ componentQrl(q_Works_component_t45qL4vNGv0);
export const NoWorks2 = /*#__PURE__*/ componentQrl(q_NoWorks2_component_JPD9t2HyEKg);
export const NoWorks3 = /*#__PURE__*/ componentQrl(q_NoWorks3_component_fc13h5yYn14);
export const DynamicDefaults = /*#__PURE__*/ componentQrl(q_DynamicDefaults_component_nZOHO1uiqf4);
export const ReferencedDefault = /*#__PURE__*/ componentQrl(q_ReferencedDefault_component_xTfXU3xq5Ow);
export { getValue as _auto_getValue };


Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;;;;;;;;;;sBAKC,QAAO;;mBAWmB,CAAA;QAAE,IAAI,KAXhC,QAAO;IAW0B,CAAA;;;;;;;;;;wCALxB,CAAC,EAAC,KAAK,EAAC;;IAChB,MAAM,cARP;IASC,QAAQ,GAAG,WATZ,OASoB,gBANpB,iBAFA,QAAO,aAGP,gBAAqB;;gCALU;;;;;;;;IAO/B,QAAQ,GAAG,WAHX,iBAFA,QAAO;IAMP;;;;IAIA,qBACC,UAAC;QAAI,IAAI;;;QAAQ,MAAM;;;QAAY,KAAK;wBAAa;;0BAAA;QAAM,QAAQ;;AAErE;2CAIU,CAAC,EAAC,KAAK,EAAC;;IAChB,MAAM,IAAM;IACZ,QAAQ,GAAG,CAAC;;mCAJqB,CAAC,EAAC,KAAK,EAAE,OAAO,EAAC,GAAG,EAAC,EAAC;IACxD,QAAQ,GAAG,CAAC;IACZ;;;IAIA,qBACC,WAAC;QAAI,OAAO;aAAQ;AAEtB;2CAIU,CAAC,EAAC,KAAK,EAAC;;IAChB,MAAM,IAAM;IACZ,QAAQ,GAAG,CAAC;;mCAJqB,CAAC,EAAC,KAAK,EAAE,QAAQ,MAAM,EAAC;IAC1D,QAAQ,GAAG,CAAC;IACZ;;;IAIA,qBACC,WAAC;QAAI,OAAO;aAAQ;AAEtB;AArCA,OAAO,MAAM,sBAAQ,4CAelB;AAEH,OAAO,MAAM,yBAAW,+CASrB;AAEH,OAAO,MAAM,yBAAW,+CASrB\"}")
Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;;;;;;;;;;sBAMC,QAAO;;mBAWmB,CAAA;QAAE,IAAI,KAXhC,QAAO;IAW0B,CAAA;;0BAeiB,2BAAA;;0BAAoB,2BAAA;;;;;;;;;;;wCApB7D,CAAC,EAAC,KAAK,EAAC;;IAChB,MAAM,cARP;IASC,QAAQ,GAAG,WATZ,OASoB,gBANpB,iBAFA,QAAO,aAGP,gBAAqB;;gCALU;;;;;;;;IAO/B,QAAQ,GAAG,WAHX,iBAFA,QAAO;IAMP;;;;IAIA,qBACC,UAAC;QAAI,IAAI;;;QAAQ,MAAM;;;QAAY,KAAK;wBAAa;;0BAAA;QAAM,QAAQ;;AAErE;2CAIU,CAAC,EAAC,KAAK,EAAC;;IAChB,MAAM,IAAM;IACZ,QAAQ,GAAG,CAAC;;mCAJqB,CAAC,EAAC,KAAK,EAAE,OAAO,EAAC,GAAG,EAAC,EAAC;IACxD,QAAQ,GAAG,CAAC;IACZ;;;IAIA,qBACC,WAAC;QAAI,OAAO;aAAQ;AAEtB;kDAIU,CAAC,EAAC,KAAK,EAAC;;IAChB,MAAM,cAH2C,6CAAA;IAIjD,MAAM,cAJ+D,8CAAA;IAKrE,QAAQ,GAAG,WAL+B,iBAAO,6CAAA,iBAAoB,8CAAA;;AA7BvE,MAAM,WAAW,IAAM;0CA6BmB;oCAAS,mBAAQ;qCAAY,mBAAe;IACrF,QAAQ,GAAG,WADuC,6CAAA;IAElD;;;;;IAKA,qBACC,WAAC;QAAI,KAAK;;;;;;;;AAEZ;4CAE4C,CAAC,EAAC,QAAQ,SAAS,OAAO,EAAE,MAAM,EAAC,iBAC9E,WAAC;QAAK;QAAO;;AAzCd,OAAO,MAAM,sBAAQ,4CAelB;AAEH,OAAO,MAAM,yBAAW,+CASrB;AAEH,OAAO,MAAM,gCAAkB,sDAU5B;AAEH,OAAO,MAAM,kCAAoB,wDAE9B\"}")
== DIAGNOSTICS ==

[]
14 changes: 10 additions & 4 deletions packages/optimizer/core/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ fn example_props_optimization() {
code: r#"
import { $, component$, useTask$ } from '@qwik.dev/core';
import { CONST } from 'const';
const getValue = () => 0;
export const Works = component$(({
count,
some = 1+2,
Expand Down Expand Up @@ -545,16 +546,21 @@ export const NoWorks2 = component$(({count, stuff: {hey}}) => {
);
});

export const NoWorks3 = component$(({count, stuff = hola()}) => {
export const DynamicDefaults = component$(({count, stuff = getValue(), other: value = getValue()}) => {
console.log(stuff);
useTask$(({track}) => {
track(() => count);
console.log(count);
track(() => stuff);
track(() => value);
console.log(count, stuff, value);
});
return (
<div class={count}>{count}</div>
<div class={stuff}>{value}</div>
);
});

export const ReferencedDefault = component$(({first = getValue(second), second}) => (
<div>{first}{second}</div>
));
"#
.to_string(),
transpile_jsx: true,
Expand Down
Loading