fix: optimize pass-through spreads to known tags

This commit is contained in:
Ryan Turnquist 2025-12-08 09:30:21 -08:00
parent 5c9eb8ea17
commit 217b6ee0c6
88 changed files with 925 additions and 106 deletions

View File

@ -0,0 +1,5 @@
---
"@marko/runtime-tags": patch
---
Optimize spreads through a wrapper tag to a known tag into direct calls

View File

@ -44,16 +44,16 @@
{
"name": "comments",
"user": {
"min": 735,
"brotli": 398
"min": 801,
"brotli": 426
},
"runtime": {
"min": 7140,
"brotli": 3125
},
"total": {
"min": 7875,
"brotli": 3523
"min": 7941,
"brotli": 3551
}
},
{

View File

@ -1,14 +1,14 @@
// size: 735 (min) 398 (brotli)
// size: 801 (min) 426 (brotli)
const $if_content__setup = ($scope) => {
($scope.a,
$if_content__comment_comments._($scope),
$if_content__id._($scope));
},
$if_content__comment_comments = _if_closure(4, 0, ($scope) =>
$input_comments($scope.a, $scope._.i),
$input_comments$1($scope.a, $scope._.i),
),
$if_content__id = _if_closure(4, 0, ($scope) =>
$input_path($scope.a, $scope._.l),
$input_path$1($scope.a, $scope._.l),
),
$for_content__id = _const(11, ($scope) => {
(_attr($scope.a, "id", $scope.l), $if_content__id($scope));
@ -52,18 +52,22 @@ const $if_content__setup = ($scope) => {
$for_content__setup,
$for_content__$params,
),
$input_comments = _const(3, ($scope) => $for($scope, [$scope.d])),
$input_path = _const(4, $for_content__input_path);
$input_comments$1 = _const(3, ($scope) => $for($scope, [$scope.d])),
$input_path$1 = _const(4, $for_content__input_path);
function $setup($scope) {
$scope.a;
($scope.a, $input_comments$1($scope.a), $input_path$1($scope.a));
}
const $input_comments = _const(3, ($scope) =>
$input_comments$1($scope.a, $scope.d),
),
$input_path = _const(4, ($scope) => $input_path$1($scope.a, $scope.e));
_template(
"b",
"<ul></ul>",
"/ b&",
$setup,
_const(2, ($scope) => {
($input_comments($scope.a, $scope.c.comments),
$input_path($scope.a, $scope.c.path));
($input_comments($scope, $scope.c.comments),
$input_path($scope, $scope.c.path));
}),
).mount();

View File

@ -1 +1 @@
{"vars":{"props":{"$empty":"e","$rest":"t","$attrTag":"n","$attrTags":"r","$attrTagIterator":"i","$_assert_hoist":"o","$forIn":"l","$forOf":"u","$forTo":"f","$forUntil":"a","$_call":"c","$stringifyClassObject":"s","$stringifyStyleObject":"d","$toDelimitedString":"h","$isEventHandler":"p","$getEventHandlerName":"g","$normalizeDynamicRenderer":"v","$decodeAccessor":"b","$toArray":"m","$push":"S","$defaultDelegator":"y","$_on":"A","$createDelegator":"N","$handleDelegated":"k","$stripSpacesAndPunctuation":"C","$nextScopeId":"_","$createScope":"w","$skipScope":"E","$findBranchWithKey":"T","$destroyBranch":"I","$destroyNestedBranches":"F","$removeAndDestroyBranch":"M","$insertBranchBefore":"x","$tempDetachBranch":"L","$walker":"$","$walk":"O","$walkInternal":"D","$branchesEnabled":"K","$isResuming":"V","$registeredValues":"G","$enableBranches":"R","$init":"B","$runResumeEffects":"H","$_resume":"U","$_var_resume":"q","$_el":"P","$_attr_input_checked":"j","$_attr_input_checked_script":"W","$_attr_input_checkedValue":"J","$_attr_input_checkedValue_script":"Q","$_attr_input_value":"X","$_attr_input_value_script":"Z","$_attr_select_value":"z","$_attr_select_value_script":"Y","$setSelectOptions":"ee","$_attr_details_or_dialog_open":"te","$_attr_details_or_dialog_open_script":"ne","$inputType":"re","$setValueAndUpdateSelection":"ie","$setCheckboxValue":"oe","$controllableDelegate":"le","$syncControllable":"ue","$handleChange":"fe","$handleFormReset":"ae","$hasValueChanged":"ce","$hasCheckboxChanged":"se","$hasSelectChanged":"de","$hasFormElementChanged":"he","$normalizeStrProp":"pe","$normalizeBoolProp":"ge","$toValueProp":"ve","$isScheduled":"be","$channel":"me","$parsers":"Se","$parseHTML":"ye","$schedule":"Ae","$flushAndWaitFrame":"Ne","$triggerMacroTask":"ke","$_let":"Ce","$_const":"_e","$_or":"we","$_for_closure":"Ee","$_if_closure":"Te","$subscribeToScopeSet":"Ie","$_closure":"Fe","$_closure_get":"Me","$_child_setup":"xe","$_var":"Le","$_return":"$e","$_return_change":"Oe","$_var_change":"De","$tagIdsByGlobal":"Ke","$_id":"Ve","$_script":"Ge","$_el_read":"Re","$traverseAllHoisted":"Be","$_hoist":"He","$createBranch":"Ue","$createAndSetupBranch":"qe","$setupBranch":"Pe","$_content":"je","$_content_resume":"We","$_content_closures":"Je","$cloneCache":"Qe","$_to_text":"Xe","$_attr":"Ze","$setAttribute":"ze","$_attr_class":"Ye","$_attr_class_items":"et","$_attr_class_item":"tt","$_attr_style":"nt","$_attr_style_items":"rt","$_attr_style_item":"it","$_text":"ot","$_text_content":"lt","$_attrs":"ut","$_attrs_content":"ft","$hasAttrAlias":"at","$_attrs_partial":"ct","$_attrs_partial_content":"st","$attrsInternal":"dt","$_attr_content":"ht","$_attrs_script":"pt","$_html":"gt","$normalizeAttrValue":"vt","$normalizeString":"bt","$_lifecycle":"mt","$removeChildNodes":"St","$insertChildNodes":"yt","$toInsertNode":"At","$_await_promise":"Nt","$_await_content":"kt","$_try":"Ct","$renderCatch":"_t","$_if":"wt","$_dynamic_tag":"Et","$_resume_dynamic_tag":"Tt","$dynamicTagScript":"It","$setConditionalRenderer":"Ft","$_for_of":"Mt","$_for_in":"xt","$_for_to":"Lt","$_for_until":"$t","$loop":"Ot","$createBranchWithTagNameOrRenderer":"Dt","$bySecondArg":"Kt","$byFirstArg":"Vt","$asyncRendersLookup":"Gt","$rendering":"Rt","$pendingRenders":"Bt","$pendingRendersLookup":"Ht","$caughtError":"Ut","$placeholderShown":"qt","$pendingEffects":"Pt","$pendingScopes":"jt","$scopeKeyOffset":"Wt","$queueRender":"Jt","$queuePendingRender":"Qt","$queueEffect":"Xt","$run":"Zt","$prepareEffects":"zt","$runEffects":"Yt","$runRenders":"en","$runRender":"tn","$_enable_catch":"nn","$$signalReset":"rn","$$signal":"on","$abort":"ln","$classIdToBranch":"un","$compat":"fn","$_template":"an","$mount":"cn","$$clickCount__script":"ta","$$clickCount":"aa","$$setup":"na","$forEach":"sn","$$if_content__setup":"sa","$$if_content__comment_comments":"ia","$$if_content__id":"ca","$$for_content__id":"la","$$for_content__input_path__OR__i":"ma","$$for_content__input_path":"oa","$$for_content__i":"ua","$$for_content__open__script":"ba","$$for_content__open":"ea","$$for_content__setup":"_a","$$for_content__comment_text":"da","$$for_content__if":"fa","$$for_content__comment_comments":"ha","$$for_content__$params":"ja","$$for_content__comment":"pa","$$for":"ra","$$input_comments":"ga","$$input_path":"ka","$_attr_nonce":"dn","$setParentBranch":"hn"}}}
{"vars":{"props":{"$empty":"e","$rest":"t","$attrTag":"n","$attrTags":"r","$attrTagIterator":"i","$_assert_hoist":"o","$forIn":"l","$forOf":"u","$forTo":"f","$forUntil":"a","$_call":"c","$stringifyClassObject":"s","$stringifyStyleObject":"d","$toDelimitedString":"h","$isEventHandler":"p","$getEventHandlerName":"g","$normalizeDynamicRenderer":"v","$decodeAccessor":"b","$toArray":"m","$push":"S","$defaultDelegator":"y","$_on":"A","$createDelegator":"N","$handleDelegated":"k","$stripSpacesAndPunctuation":"C","$nextScopeId":"_","$createScope":"w","$skipScope":"E","$findBranchWithKey":"T","$destroyBranch":"I","$destroyNestedBranches":"F","$removeAndDestroyBranch":"M","$insertBranchBefore":"x","$tempDetachBranch":"L","$walker":"$","$walk":"O","$walkInternal":"D","$branchesEnabled":"K","$isResuming":"V","$registeredValues":"G","$enableBranches":"R","$init":"B","$runResumeEffects":"H","$_resume":"U","$_var_resume":"q","$_el":"P","$_attr_input_checked":"j","$_attr_input_checked_script":"W","$_attr_input_checkedValue":"J","$_attr_input_checkedValue_script":"Q","$_attr_input_value":"X","$_attr_input_value_script":"Z","$_attr_select_value":"z","$_attr_select_value_script":"Y","$setSelectOptions":"ee","$_attr_details_or_dialog_open":"te","$_attr_details_or_dialog_open_script":"ne","$inputType":"re","$setValueAndUpdateSelection":"ie","$setCheckboxValue":"oe","$controllableDelegate":"le","$syncControllable":"ue","$handleChange":"fe","$handleFormReset":"ae","$hasValueChanged":"ce","$hasCheckboxChanged":"se","$hasSelectChanged":"de","$hasFormElementChanged":"he","$normalizeStrProp":"pe","$normalizeBoolProp":"ge","$toValueProp":"ve","$isScheduled":"be","$channel":"me","$parsers":"Se","$parseHTML":"ye","$schedule":"Ae","$flushAndWaitFrame":"Ne","$triggerMacroTask":"ke","$_let":"Ce","$_const":"_e","$_or":"we","$_for_closure":"Ee","$_if_closure":"Te","$subscribeToScopeSet":"Ie","$_closure":"Fe","$_closure_get":"Me","$_child_setup":"xe","$_var":"Le","$_return":"$e","$_return_change":"Oe","$_var_change":"De","$tagIdsByGlobal":"Ke","$_id":"Ve","$_script":"Ge","$_el_read":"Re","$traverseAllHoisted":"Be","$_hoist":"He","$createBranch":"Ue","$createAndSetupBranch":"qe","$setupBranch":"Pe","$_content":"je","$_content_resume":"We","$_content_closures":"Je","$cloneCache":"Qe","$_to_text":"Xe","$_attr":"Ze","$setAttribute":"ze","$_attr_class":"Ye","$_attr_class_items":"et","$_attr_class_item":"tt","$_attr_style":"nt","$_attr_style_items":"rt","$_attr_style_item":"it","$_text":"ot","$_text_content":"lt","$_attrs":"ut","$_attrs_content":"ft","$hasAttrAlias":"at","$_attrs_partial":"ct","$_attrs_partial_content":"st","$attrsInternal":"dt","$_attr_content":"ht","$_attrs_script":"pt","$_html":"gt","$normalizeAttrValue":"vt","$normalizeString":"bt","$_lifecycle":"mt","$removeChildNodes":"St","$insertChildNodes":"yt","$toInsertNode":"At","$_await_promise":"Nt","$_await_content":"kt","$_try":"Ct","$renderCatch":"_t","$_if":"wt","$_dynamic_tag":"Et","$_resume_dynamic_tag":"Tt","$dynamicTagScript":"It","$setConditionalRenderer":"Ft","$_for_of":"Mt","$_for_in":"xt","$_for_to":"Lt","$_for_until":"$t","$loop":"Ot","$createBranchWithTagNameOrRenderer":"Dt","$bySecondArg":"Kt","$byFirstArg":"Vt","$asyncRendersLookup":"Gt","$rendering":"Rt","$pendingRenders":"Bt","$pendingRendersLookup":"Ht","$caughtError":"Ut","$placeholderShown":"qt","$pendingEffects":"Pt","$pendingScopes":"jt","$scopeKeyOffset":"Wt","$queueRender":"Jt","$queuePendingRender":"Qt","$queueEffect":"Xt","$run":"Zt","$prepareEffects":"zt","$runEffects":"Yt","$runRenders":"en","$runRender":"tn","$_enable_catch":"nn","$$signalReset":"rn","$$signal":"on","$abort":"ln","$classIdToBranch":"un","$compat":"fn","$_template":"an","$mount":"cn","$$clickCount__script":"ta","$$clickCount":"aa","$$setup":"na","$forEach":"sn","$$if_content__setup":"sa","$$if_content__comment_comments":"ia","$$if_content__id":"ca","$$for_content__id":"la","$$for_content__input_path__OR__i":"ma","$$for_content__input_path":"oa","$$for_content__i":"ua","$$for_content__open__script":"ba","$$for_content__open":"ea","$$for_content__setup":"_a","$$for_content__comment_text":"da","$$for_content__if":"fa","$$for_content__comment_comments":"ha","$$for_content__$params":"ja","$$for_content__comment":"pa","$$for":"ra","$$input_comments":"ga","$$input_path":"ka","$_attr_nonce":"dn","$setParentBranch":"hn","$$input_comments$1":"$a","$$input_path$1":"qa"}}}

View File

@ -3,10 +3,14 @@ export const $walks = /* <comments> */`/${_comments_walks}&`;
import { $setup as _comments, $input_comments as _comments_input_comments, $input_path as _comments_input_path, $template as _comments_template, $walks as _comments_walks } from "./tags/comments.marko";
export function $setup($scope) {
_comments($scope["#childScope/0"]);
_comments_input_comments($scope["#childScope/0"]);
_comments_input_path($scope["#childScope/0"]);
}
import * as _ from "@marko/runtime-tags/debug/dom";
export const $input_comments = /* @__PURE__ */_._const("input_comments", $scope => _comments_input_comments($scope["#childScope/0"], $scope.input_comments));
export const $input_path = /* @__PURE__ */_._const("input_path", $scope => _comments_input_path($scope["#childScope/0"], $scope.input_path));
export const $input = /* @__PURE__ */_._const("input", $scope => {
_comments_input_comments($scope["#childScope/0"], $scope.input.comments);
_comments_input_path($scope["#childScope/0"], $scope.input.path);
$input_comments($scope, $scope.input.comments);
$input_path($scope, $scope.input.path);
});
export default /* @__PURE__ */_._template("__tests__/template.marko", $template, $walks, $setup, $input);

View File

@ -5,12 +5,12 @@ export default _._template("__tests__/template.marko", input => {
const $scope0_id = _._scope_id();
const $childScope = _._peek_scope_id();
_._set_serialize_reason({
/* input.comments, input.path */0: _._serialize_guard($scope0_reason, /* input */0),
/* input.comments */1: _._serialize_guard($scope0_reason, /* input */0),
/* input.path */2: _._serialize_guard($scope0_reason, /* input */0)
/* input.comments, input.path */0: _._serialize_guard($scope0_reason, /* input.comments, input.path */0),
/* input.comments */1: _._serialize_guard($scope0_reason, /* input.comments */1),
/* input.path */2: _._serialize_guard($scope0_reason, /* input.path */2)
});
_comments(input);
_._serialize_if($scope0_reason, /* input */0) && _._scope($scope0_id, {
"#childScope/0": _._serialize_if($scope0_reason, /* input */0) && _._existing_scope($childScope)
_._serialize_if($scope0_reason, /* input.comments, input.path */0) && _._scope($scope0_id, {
"#childScope/0": _._serialize_if($scope0_reason, /* input.comments, input.path */0) && _._existing_scope($childScope)
}, "__tests__/template.marko", 0);
});

View File

@ -0,0 +1,8 @@
{
"vars": {
"props": {
"$_": "r",
"$init": "t"
}
}
}

View File

@ -0,0 +1,7 @@
# Render
```html
<input
class="foo"
value="pass"
/>
```

View File

@ -0,0 +1,12 @@
# Render
```html
<input
class="foo"
value="pass"
/>
```
# Mutations
```
INSERT input
```

View File

@ -0,0 +1,11 @@
export const $template = "<input>";
export const $walks = /* get, over(1) */" b";
export const $setup = () => {};
import * as _ from "@marko/runtime-tags/debug/dom";
export const $input_class = /* @__PURE__ */_._const("input_class", $scope => _._attr_class($scope["#input/0"], $scope.input_class));
export const $input_value = /* @__PURE__ */_._const("input_value", $scope => _._attr($scope["#input/0"], "value", $scope.input_value));
export const $input = /* @__PURE__ */_._const("input", $scope => {
$input_class($scope, $scope.input.class);
$input_value($scope, $scope.input.value);
});
export default /* @__PURE__ */_._template("__tests__/tags/child.marko", $template, $walks, $setup, $input);

View File

@ -0,0 +1,12 @@
export const $template = _child_template;
export const $walks = /* <child> */`/${_child_walks}&`;
import { $setup as _child, $input_value as _child_input_value, $input_class as _child_input_class, $template as _child_template, $walks as _child_walks } from "./child.marko";
export function $setup($scope) {
_child($scope["#childScope/0"]);
_child_input_value($scope["#childScope/0"], "pass");
_child_input_class($scope["#childScope/0"]);
}
import * as _ from "@marko/runtime-tags/debug/dom";
export const $input_class = /* @__PURE__ */_._const("input_class", $scope => _child_input_class($scope["#childScope/0"], $scope.input_class));
export const $input = /* @__PURE__ */_._const("input", $scope => $input_class($scope, $scope.input.class));
export default /* @__PURE__ */_._template("__tests__/tags/wrap.marko", $template, $walks, $setup, $input);

View File

@ -0,0 +1,9 @@
export const $template = _wrap_template;
export const $walks = /* <wrap> */`/${_wrap_walks}&`;
import { $setup as _wrap, $input_class as _wrap_input_class, $template as _wrap_template, $walks as _wrap_walks } from "./tags/wrap.marko";
export function $setup($scope) {
_wrap($scope["#childScope/0"]);
_wrap_input_class($scope["#childScope/0"], "foo");
}
import * as _ from "@marko/runtime-tags/debug/dom";
export default /* @__PURE__ */_._template("__tests__/template.marko", $template, $walks, $setup);

View File

@ -0,0 +1,7 @@
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/tags/child.marko", input => {
const $scope0_reason = _._scope_reason();
const $scope0_id = _._scope_id();
_._html(`<input${_._attr_class(input.class)}${_._attr("value", input.value)}>${_._el_resume($scope0_id, "#input/0", _._serialize_guard($scope0_reason, /* input.class, input.value */0))}`);
_._serialize_if($scope0_reason, /* input.class, input.value */0) && _._scope($scope0_id, {}, "__tests__/tags/child.marko", 0);
});

View File

@ -0,0 +1,15 @@
import _child from "./child.marko";
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/tags/wrap.marko", input => {
const $scope0_reason = _._scope_reason();
const $scope0_id = _._scope_id();
const $childScope = _._peek_scope_id();
_._set_serialize_reason(_._serialize_guard($scope0_reason, /* input.class */0));
_child({
...input,
value: "pass"
});
_._serialize_if($scope0_reason, /* input.class */0) && _._scope($scope0_id, {
"#childScope/0": _._serialize_if($scope0_reason, /* input.class */0) && _._existing_scope($childScope)
}, "__tests__/tags/wrap.marko", 0);
});

View File

@ -0,0 +1,8 @@
import _wrap from "./tags/wrap.marko";
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/template.marko", input => {
const $scope0_id = _._scope_id();
_wrap({
class: "foo"
});
});

View File

@ -0,0 +1,7 @@
# Render
```html
<input
class="foo"
value="pass"
/>
```

View File

@ -0,0 +1,12 @@
# Render
```html
<html>
<head />
<body>
<input
class="foo"
value="pass"
/>
</body>
</html>
```

View File

@ -0,0 +1,7 @@
# Render End
```html
<input
class="foo"
value="pass"
/>
```

View File

@ -0,0 +1,25 @@
# Write
```html
<input class=foo value=pass>
```
# Render End
```html
<html>
<head />
<body>
<input
class="foo"
value="pass"
/>
</body>
</html>
```
# Mutations
```
INSERT html
INSERT html/head
INSERT html/body
INSERT html/body/input
```

View File

@ -0,0 +1 @@
<input class=input.class value=input.value>

View File

@ -0,0 +1 @@
<child ...input value="pass" />

View File

@ -0,0 +1 @@
<wrap class="foo" value="fail" />

View File

@ -0,0 +1 @@
export const steps = [{}];

View File

@ -0,0 +1,5 @@
{
"vars": {
"props": {}
}
}

View File

@ -0,0 +1,9 @@
# Render
```html
<input
class="foo"
/>
<input
class="bar"
/>
```

View File

@ -0,0 +1,14 @@
# Render
```html
<input
class="foo"
/>
<input
class="bar"
/>
```
# Mutations
```
INSERT input0, input1
```

View File

@ -0,0 +1,11 @@
export const $template = "<input>";
export const $walks = /* get, over(1) */" b";
export const $setup = () => {};
import * as _ from "@marko/runtime-tags/debug/dom";
export const $input_class = /* @__PURE__ */_._const("input_class", $scope => _._attr_class($scope["#input/0"], $scope.input_class));
export const $input_value = /* @__PURE__ */_._const("input_value", $scope => _._attr($scope["#input/0"], "value", $scope.input_value));
export const $input = /* @__PURE__ */_._const("input", $scope => {
$input_class($scope, $scope.input.class);
$input_value($scope, $scope.input.value);
});
export default /* @__PURE__ */_._template("__tests__/tags/child.marko", $template, $walks, $setup, $input);

View File

@ -0,0 +1,18 @@
export const $template = _child_template;
export const $walks = /* <child> */`/${_child_walks}&`;
import { $setup as _child, $input_class as _child_input_class, $input_value as _child_input_value, $template as _child_template, $walks as _child_walks } from "./child.marko";
export function $setup($scope) {
_child($scope["#childScope/0"]);
}
import * as _ from "@marko/runtime-tags/debug/dom";
export const $input = /* @__PURE__ */_._const("input", $scope => {
const $child_input_spread = {
...$scope.input,
...{
a: 1
}
};
_child_input_class($scope["#childScope/0"], $child_input_spread.class);
_child_input_value($scope["#childScope/0"], $child_input_spread.value);
});
export default /* @__PURE__ */_._template("__tests__/tags/wrap-many-spreads.marko", $template, $walks, $setup, $input);

View File

@ -0,0 +1,20 @@
export const $template = _child_template;
export const $walks = /* <child> */`/${_child_walks}&`;
import { $setup as _child, $input_class as _child_input_class, $input_value as _child_input_value, $template as _child_template, $walks as _child_walks } from "./child.marko";
export function $setup($scope) {
_child($scope["#childScope/0"]);
_child_input_class($scope["#childScope/0"]);
_child_input_value($scope["#childScope/0"]);
}
import * as _ from "@marko/runtime-tags/debug/dom";
export const $rest_class = /* @__PURE__ */_._const("rest_class", $scope => _child_input_class($scope["#childScope/0"], $scope.rest_class));
export const $rest_value = /* @__PURE__ */_._const("rest_value", $scope => _child_input_value($scope["#childScope/0"], $scope.rest_value));
export const $input = /* @__PURE__ */_._const("input", $scope => (({
value,
...rest
}) => $rest($scope, rest))($scope.input));
export const $rest = /* @__PURE__ */_._const("rest", $scope => {
$rest_class($scope, $scope.rest.class);
$rest_value($scope, $scope.rest.value);
});
export default /* @__PURE__ */_._template("__tests__/tags/wrap-rest.marko", $template, $walks, $setup, $input);

View File

@ -0,0 +1,18 @@
export const $template = _child_template;
export const $walks = /* <child> */`/${_child_walks}&`;
import { $setup as _child, $input_class as _child_input_class, $input_value as _child_input_value, $template as _child_template, $walks as _child_walks } from "./child.marko";
export function $setup($scope) {
_child($scope["#childScope/0"]);
}
import * as _ from "@marko/runtime-tags/debug/dom";
export const $input = /* @__PURE__ */_._const("input", $scope => {
const $child_input_spread = {
...$scope.input,
...{
a: 1
}
};
_child_input_class($scope["#childScope/0"], $child_input_spread.class);
_child_input_value($scope["#childScope/0"], $child_input_spread.value);
});
export default /* @__PURE__ */_._template("__tests__/tags/wrap.marko", $template, $walks, $setup, $input);

View File

@ -0,0 +1,16 @@
export const $template = `${_wrapManySpreads_template}${_wrapRest_template}`;
export const $walks = /* <wrap-many-spreads>, <wrap-rest> */`/${_wrapManySpreads_walks}&/${_wrapRest_walks}&`;
import { $setup as _wrapManySpreads, $input as _wrapManySpreads_input, $template as _wrapManySpreads_template, $walks as _wrapManySpreads_walks } from "./tags/wrap-many-spreads.marko";
import { $setup as _wrapRest, $rest as _wrapRest_input_$rest, $template as _wrapRest_template, $walks as _wrapRest_walks } from "./tags/wrap-rest.marko";
export function $setup($scope) {
_wrapManySpreads($scope["#childScope/0"]);
_wrapManySpreads_input($scope["#childScope/0"], {
class: "foo"
});
_wrapRest($scope["#childScope/1"]);
_wrapRest_input_$rest($scope["#childScope/1"], {
class: "bar"
});
}
import * as _ from "@marko/runtime-tags/debug/dom";
export default /* @__PURE__ */_._template("__tests__/template.marko", $template, $walks, $setup);

View File

@ -0,0 +1,7 @@
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/tags/child.marko", input => {
const $scope0_reason = _._scope_reason();
const $scope0_id = _._scope_id();
_._html(`<input${_._attr_class(input.class)}${_._attr("value", input.value)}>${_._el_resume($scope0_id, "#input/0", _._serialize_guard($scope0_reason, /* input.class, input.value */0))}`);
_._serialize_if($scope0_reason, /* input.class, input.value */0) && _._scope($scope0_id, {}, "__tests__/tags/child.marko", 0);
});

View File

@ -0,0 +1,17 @@
import _child from "./child.marko";
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/tags/wrap-many-spreads.marko", input => {
const $scope0_reason = _._scope_reason();
const $scope0_id = _._scope_id();
const $childScope = _._peek_scope_id();
_._set_serialize_reason(_._serialize_guard($scope0_reason, /* input */0));
_child({
...input,
...{
a: 1
}
});
_._serialize_if($scope0_reason, /* input */0) && _._scope($scope0_id, {
"#childScope/0": _._serialize_if($scope0_reason, /* input */0) && _._existing_scope($childScope)
}, "__tests__/tags/wrap-many-spreads.marko", 0);
});

View File

@ -0,0 +1,16 @@
import _child from "./child.marko";
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/tags/wrap-rest.marko", input => {
const $scope0_reason = _._scope_reason();
const $scope0_id = _._scope_id();
const {
value,
...rest
} = input;
const $childScope = _._peek_scope_id();
_._set_serialize_reason(_._serialize_guard($scope0_reason, /* rest.class, rest.value */0));
_child(rest);
_._serialize_if($scope0_reason, /* rest.class, rest.value */0) && _._scope($scope0_id, {
"#childScope/0": _._serialize_if($scope0_reason, /* rest.class, rest.value */0) && _._existing_scope($childScope)
}, "__tests__/tags/wrap-rest.marko", 0);
});

View File

@ -0,0 +1,17 @@
import _child from "./child.marko";
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/tags/wrap.marko", input => {
const $scope0_reason = _._scope_reason();
const $scope0_id = _._scope_id();
const $childScope = _._peek_scope_id();
_._set_serialize_reason(_._serialize_guard($scope0_reason, /* input */0));
_child({
...input,
...{
a: 1
}
});
_._serialize_if($scope0_reason, /* input */0) && _._scope($scope0_id, {
"#childScope/0": _._serialize_if($scope0_reason, /* input */0) && _._existing_scope($childScope)
}, "__tests__/tags/wrap.marko", 0);
});

View File

@ -0,0 +1,12 @@
import _wrapManySpreads from "./tags/wrap-many-spreads.marko";
import _wrapRest from "./tags/wrap-rest.marko";
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/template.marko", input => {
const $scope0_id = _._scope_id();
_wrapManySpreads({
class: "foo"
});
_wrapRest({
class: "bar"
});
});

View File

@ -0,0 +1,9 @@
# Render
```html
<input
class="foo"
/>
<input
class="bar"
/>
```

View File

@ -0,0 +1,14 @@
# Render
```html
<html>
<head />
<body>
<input
class="foo"
/>
<input
class="bar"
/>
</body>
</html>
```

View File

@ -0,0 +1,9 @@
# Render End
```html
<input
class="foo"
/>
<input
class="bar"
/>
```

View File

@ -0,0 +1,28 @@
# Write
```html
<input class=foo><input class=bar>
```
# Render End
```html
<html>
<head />
<body>
<input
class="foo"
/>
<input
class="bar"
/>
</body>
</html>
```
# Mutations
```
INSERT html
INSERT html/head
INSERT html/body
INSERT html/body/input0
INSERT html/body/input1
```

View File

@ -0,0 +1 @@
<input class=input.class value=input.value>

View File

@ -0,0 +1 @@
<child ...input ...{ a: 1 }/>

View File

@ -0,0 +1,2 @@
<const/{ value, ...rest }=input>
<child ...rest />

View File

@ -0,0 +1,2 @@
<wrap-many-spreads class="foo" />
<wrap-rest class="bar" />

View File

@ -0,0 +1 @@
export const steps = [{}];

View File

@ -0,0 +1,8 @@
{
"vars": {
"props": {
"$_": "r",
"$init": "t"
}
}
}

View File

@ -0,0 +1,6 @@
# Render
```html
<input
class="foo"
/>
```

View File

@ -0,0 +1,11 @@
# Render
```html
<input
class="foo"
/>
```
# Mutations
```
INSERT input
```

View File

@ -0,0 +1,11 @@
export const $template = "<input>";
export const $walks = /* get, over(1) */" b";
export const $setup = () => {};
import * as _ from "@marko/runtime-tags/debug/dom";
export const $input_class = /* @__PURE__ */_._const("input_class", $scope => _._attr_class($scope["#input/0"], $scope.input_class));
export const $input_value = /* @__PURE__ */_._const("input_value", $scope => _._attr($scope["#input/0"], "value", $scope.input_value));
export const $input = /* @__PURE__ */_._const("input", $scope => {
$input_class($scope, $scope.input.class);
$input_value($scope, $scope.input.value);
});
export default /* @__PURE__ */_._template("__tests__/tags/child.marko", $template, $walks, $setup, $input);

View File

@ -0,0 +1,16 @@
export const $template = _child_template;
export const $walks = /* <child> */`/${_child_walks}&`;
import { $setup as _child, $input_class as _child_input_class, $input_value as _child_input_value, $template as _child_template, $walks as _child_walks } from "./child.marko";
export function $setup($scope) {
_child($scope["#childScope/0"]);
_child_input_class($scope["#childScope/0"]);
_child_input_value($scope["#childScope/0"]);
}
import * as _ from "@marko/runtime-tags/debug/dom";
export const $input_class = /* @__PURE__ */_._const("input_class", $scope => _child_input_class($scope["#childScope/0"], $scope.input_class));
export const $input_value = /* @__PURE__ */_._const("input_value", $scope => _child_input_value($scope["#childScope/0"], $scope.input_value));
export const $input = /* @__PURE__ */_._const("input", $scope => {
$input_class($scope, $scope.input.class);
$input_value($scope, $scope.input.value);
});
export default /* @__PURE__ */_._template("__tests__/tags/wrap.marko", $template, $walks, $setup, $input);

View File

@ -0,0 +1,10 @@
export const $template = _wrap_template;
export const $walks = /* <wrap> */`/${_wrap_walks}&`;
import { $setup as _wrap, $input_class as _wrap_input_class, $input_value as _wrap_input_value, $template as _wrap_template, $walks as _wrap_walks } from "./tags/wrap.marko";
export function $setup($scope) {
_wrap($scope["#childScope/0"]);
_wrap_input_class($scope["#childScope/0"], "foo");
_wrap_input_value($scope["#childScope/0"]);
}
import * as _ from "@marko/runtime-tags/debug/dom";
export default /* @__PURE__ */_._template("__tests__/template.marko", $template, $walks, $setup);

View File

@ -0,0 +1,7 @@
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/tags/child.marko", input => {
const $scope0_reason = _._scope_reason();
const $scope0_id = _._scope_id();
_._html(`<input${_._attr_class(input.class)}${_._attr("value", input.value)}>${_._el_resume($scope0_id, "#input/0", _._serialize_guard($scope0_reason, /* input.class, input.value */0))}`);
_._serialize_if($scope0_reason, /* input.class, input.value */0) && _._scope($scope0_id, {}, "__tests__/tags/child.marko", 0);
});

View File

@ -0,0 +1,12 @@
import _child from "./child.marko";
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/tags/wrap.marko", input => {
const $scope0_reason = _._scope_reason();
const $scope0_id = _._scope_id();
const $childScope = _._peek_scope_id();
_._set_serialize_reason(_._serialize_guard($scope0_reason, /* input.class, input.value */0));
_child(input);
_._serialize_if($scope0_reason, /* input.class, input.value */0) && _._scope($scope0_id, {
"#childScope/0": _._serialize_if($scope0_reason, /* input.class, input.value */0) && _._existing_scope($childScope)
}, "__tests__/tags/wrap.marko", 0);
});

View File

@ -0,0 +1,8 @@
import _wrap from "./tags/wrap.marko";
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/template.marko", input => {
const $scope0_id = _._scope_id();
_wrap({
class: "foo"
});
});

View File

@ -0,0 +1,6 @@
# Render
```html
<input
class="foo"
/>
```

View File

@ -0,0 +1,11 @@
# Render
```html
<html>
<head />
<body>
<input
class="foo"
/>
</body>
</html>
```

View File

@ -0,0 +1,6 @@
# Render End
```html
<input
class="foo"
/>
```

View File

@ -0,0 +1,24 @@
# Write
```html
<input class=foo>
```
# Render End
```html
<html>
<head />
<body>
<input
class="foo"
/>
</body>
</html>
```
# Mutations
```
INSERT html
INSERT html/head
INSERT html/body
INSERT html/body/input
```

View File

@ -0,0 +1 @@
<input class=input.class value=input.value>

View File

@ -0,0 +1 @@
<child ...input/>

View File

@ -0,0 +1 @@
<wrap class="foo" />

View File

@ -0,0 +1 @@
export const steps = [{}];

View File

@ -0,0 +1,5 @@
{
"vars": {
"props": {}
}
}

View File

@ -0,0 +1,9 @@
# Render
```html
<input
class="foo"
/>
<input
class="foo"
/>
```

View File

@ -0,0 +1,14 @@
# Render
```html
<input
class="foo"
/>
<input
class="foo"
/>
```
# Mutations
```
INSERT input0, input1
```

View File

@ -0,0 +1,11 @@
export const $template = "<input>";
export const $walks = /* get, over(1) */" b";
export const $setup = () => {};
import * as _ from "@marko/runtime-tags/debug/dom";
export const $input_class = /* @__PURE__ */_._const("input_class", $scope => _._attr_class($scope["#input/0"], $scope.input_class));
export const $input_value = /* @__PURE__ */_._const("input_value", $scope => _._attr($scope["#input/0"], "value", $scope.input_value));
export const $input = /* @__PURE__ */_._const("input", $scope => {
$input_class($scope, $scope.input.class);
$input_value($scope, $scope.input.value);
});
export default /* @__PURE__ */_._template("__tests__/tags/child.marko", $template, $walks, $setup, $input);

View File

@ -0,0 +1,25 @@
export const $template = `${_child_template}${_child_template}`;
export const $walks = /* <child>, <child> */`/${_child_walks}&/${_child_walks}&`;
import { $setup as _child, $input_class as _child_input_class, $input_value as _child_input_value, $template as _child_template, $walks as _child_walks } from "./child.marko";
export function $setup($scope) {
_child($scope["#childScope/0"]);
_child_input_class($scope["#childScope/0"]);
_child_input_value($scope["#childScope/0"]);
_child($scope["#childScope/1"]);
_child_input_class($scope["#childScope/1"]);
_child_input_value($scope["#childScope/1"]);
}
import * as _ from "@marko/runtime-tags/debug/dom";
export const $input_class = /* @__PURE__ */_._const("input_class", $scope => {
_child_input_class($scope["#childScope/0"], $scope.input_class);
_child_input_class($scope["#childScope/1"], $scope.input_class);
});
export const $input_value = /* @__PURE__ */_._const("input_value", $scope => {
_child_input_value($scope["#childScope/0"], $scope.input_value);
_child_input_value($scope["#childScope/1"], $scope.input_value);
});
export const $input = /* @__PURE__ */_._const("input", $scope => {
$input_class($scope, $scope.input.class);
$input_value($scope, $scope.input.value);
});
export default /* @__PURE__ */_._template("__tests__/tags/wrap.marko", $template, $walks, $setup, $input);

View File

@ -0,0 +1,10 @@
export const $template = _wrap_template;
export const $walks = /* <wrap> */`/${_wrap_walks}&`;
import { $setup as _wrap, $input_class as _wrap_input_class, $input_value as _wrap_input_value, $template as _wrap_template, $walks as _wrap_walks } from "./tags/wrap.marko";
export function $setup($scope) {
_wrap($scope["#childScope/0"]);
_wrap_input_class($scope["#childScope/0"], "foo");
_wrap_input_value($scope["#childScope/0"]);
}
import * as _ from "@marko/runtime-tags/debug/dom";
export default /* @__PURE__ */_._template("__tests__/template.marko", $template, $walks, $setup);

View File

@ -0,0 +1,7 @@
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/tags/child.marko", input => {
const $scope0_reason = _._scope_reason();
const $scope0_id = _._scope_id();
_._html(`<input${_._attr_class(input.class)}${_._attr("value", input.value)}>${_._el_resume($scope0_id, "#input/0", _._serialize_guard($scope0_reason, /* input.class, input.value */0))}`);
_._serialize_if($scope0_reason, /* input.class, input.value */0) && _._scope($scope0_id, {}, "__tests__/tags/child.marko", 0);
});

View File

@ -0,0 +1,16 @@
import _child from "./child.marko";
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/tags/wrap.marko", input => {
const $scope0_reason = _._scope_reason();
const $scope0_id = _._scope_id();
const $childScope = _._peek_scope_id();
_._set_serialize_reason(_._serialize_guard($scope0_reason, /* input.class, input.value */0));
_child(input);
const $childScope2 = _._peek_scope_id();
_._set_serialize_reason(_._serialize_guard($scope0_reason, /* input.class, input.value */0));
_child(input);
_._serialize_if($scope0_reason, /* input.class, input.value */0) && _._scope($scope0_id, {
"#childScope/0": _._serialize_if($scope0_reason, /* input.class, input.value */0) && _._existing_scope($childScope),
"#childScope/1": _._serialize_if($scope0_reason, /* input.class, input.value */0) && _._existing_scope($childScope2)
}, "__tests__/tags/wrap.marko", 0);
});

View File

@ -0,0 +1,8 @@
import _wrap from "./tags/wrap.marko";
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/template.marko", input => {
const $scope0_id = _._scope_id();
_wrap({
class: "foo"
});
});

View File

@ -0,0 +1,9 @@
# Render
```html
<input
class="foo"
/>
<input
class="foo"
/>
```

View File

@ -0,0 +1,14 @@
# Render
```html
<html>
<head />
<body>
<input
class="foo"
/>
<input
class="foo"
/>
</body>
</html>
```

View File

@ -0,0 +1,9 @@
# Render End
```html
<input
class="foo"
/>
<input
class="foo"
/>
```

View File

@ -0,0 +1,28 @@
# Write
```html
<input class=foo><input class=foo>
```
# Render End
```html
<html>
<head />
<body>
<input
class="foo"
/>
<input
class="foo"
/>
</body>
</html>
```
# Mutations
```
INSERT html
INSERT html/head
INSERT html/body
INSERT html/body/input0
INSERT html/body/input1
```

View File

@ -0,0 +1 @@
<input class=input.class value=input.value>

View File

@ -0,0 +1,2 @@
<child ...input/>
<child ...input/>

View File

@ -0,0 +1 @@
<wrap class="foo" />

View File

@ -0,0 +1 @@
export const steps = [{}];

View File

@ -7,7 +7,8 @@
"$$partialObj": "n",
"$$partialObj_a": "r",
"$$a": "a",
"$$partialObj_b": "e"
"$$partialObj_b": "e",
"$$obj_b": "m"
}
}
}

View File

@ -5,7 +5,7 @@ const $obj = _._let(6, ($scope) => {
$partialObj($scope, partialObj);
})($scope.g),
$a($scope, $scope.g.a),
$partialObj_b($scope, $scope.g.b));
$obj_b($scope, $scope.g.b));
}),
$partialObj = _._const(8, ($scope) => {
(_._text($scope.b, JSON.stringify($scope.i)),
@ -15,7 +15,7 @@ const $obj = _._let(6, ($scope) => {
_._text($scope.e, void 0 === $scope.k ? "removed a" : "didn't remove a"),
),
$a = _._const(7, ($scope) => _._text($scope.c, $scope.h)),
$partialObj_b = _._const(9, ($scope) => _._text($scope.d, $scope.j));
$obj_b = _._const(9, ($scope) => _._text($scope.d, $scope.j));
(_._script("a0", ($scope) =>
_._on($scope.f, "click", function () {
$obj($scope, { a: 4, b: 5, d: 6 });

View File

@ -8,7 +8,7 @@ const $obj = /* @__PURE__ */_._let("obj/6", $scope => {
...partialObj
}) => $partialObj($scope, partialObj))($scope.obj);
$a($scope, $scope.obj.a);
$partialObj_b($scope, $scope.obj.b);
$obj_b($scope, $scope.obj.b);
});
const $partialObj = /* @__PURE__ */_._const("partialObj", $scope => {
_._text($scope["#text/1"], JSON.stringify($scope.partialObj));
@ -16,7 +16,7 @@ const $partialObj = /* @__PURE__ */_._const("partialObj", $scope => {
});
const $partialObj_a = /* @__PURE__ */_._const("partialObj_a", $scope => _._text($scope["#text/4"], $scope.partialObj_a === undefined ? "removed a" : "didn't remove a"));
const $a = /* @__PURE__ */_._const("a", $scope => _._text($scope["#text/2"], $scope.a));
const $partialObj_b = /* @__PURE__ */_._const("partialObj_b", $scope => _._text($scope["#text/3"], $scope.partialObj_b));
const $obj_b = /* @__PURE__ */_._const("obj_b", $scope => _._text($scope["#text/3"], $scope.obj_b));
const $setup__script = _._script("__tests__/template.marko_0", $scope => _._on($scope["#button/5"], "click", function () {
$obj($scope, {
a: 4,

View File

@ -17,17 +17,21 @@ import {
} from "./nested-attribute-tags";
import { filterMap, forEach, fromIter, type Opt } from "./optional";
import {
addRead,
type Binding,
BindingType,
bindingUtil,
createBinding,
dropRead,
dropReferences,
getAllTagReferenceNodes,
getDebugNames,
getOrCreatePropertyAlias,
getScopeAccessorLiteral,
type InputBinding,
mergeReferences,
type ParamBinding,
type ReferencedExtra,
setBindingDownstream,
trackParamsReferences,
trackVarReferences,
@ -584,11 +588,9 @@ function analyzeAttrs(
bindings = bindingUtil.add(bindings, templateExportAttr.binding);
}
if (bindings) {
forEach(bindings, (binding) => {
setBindingDownstream(binding, groupExtra);
});
}
forEach(bindings, (binding) => {
setBindingDownstream(binding, groupExtra);
});
for (const name of group) {
known[attrTagLookup[name].name] = groupKnownValue;
@ -604,8 +606,9 @@ function analyzeAttrs(
}
}
const { attributes } = tag.node;
let knownSpreadBinding: Binding | undefined;
let spreadReferenceNodes: t.Node[] | undefined;
const { attributes } = tag.node;
for (let i = attributes.length; i--; ) {
const attr = attributes[i];
if (t.isMarkoAttribute(attr)) {
@ -616,17 +619,29 @@ function analyzeAttrs(
continue;
}
const attrExtra = (attr.value.extra ??= {}) as ReferencedExtra;
seen.add(attr.name);
setBindingDownstream(
templateExportAttr.binding,
(attr.value.extra ??= {}),
);
setBindingDownstream(templateExportAttr.binding, attrExtra);
if (knownSpreadBinding) {
addRead(
attrExtra,
undefined,
getOrCreatePropertyAlias(knownSpreadBinding, attr.name),
);
}
}
if (spreadReferenceNodes) {
spreadReferenceNodes.push(attr.value);
} else if (t.isMarkoSpreadAttribute(attr)) {
spreadReferenceNodes = [attr.value];
if (!propTree.rest) {
knownSpreadBinding = getSingleSpreadBinding(attributes);
dropRead(attr.value.extra as ReferencedExtra);
}
if (!knownSpreadBinding) {
spreadReferenceNodes = [attr.value];
}
} else {
const attrValueExtra = (attr.value.extra ??= {});
known[attr.name] = { value: attrValueExtra };
@ -634,36 +649,49 @@ function analyzeAttrs(
}
}
if (spreadReferenceNodes) {
const extra = (inputExpr.value = mergeReferences(
section,
tag.node,
spreadReferenceNodes,
));
let spreadBinding = propTree.binding;
if (seen.size) {
spreadBinding = createBinding(
generateUid(`${getTagName(tag)}_attrs`),
spreadBinding.type,
spreadBinding.section,
spreadBinding,
undefined,
fromIter(seen),
spreadReferenceNodes[0].loc,
true,
);
}
if (knownSpreadBinding) {
for (const prop in propTree.props) {
if (!seen.has(prop)) {
const propBinding = getOrCreatePropertyAlias(knownSpreadBinding, prop);
const propExtra: ReferencedExtra = { section };
setBindingDownstream(spreadBinding, extra);
known[prop] = { value: propExtra };
rootAttrExprs.add(propExtra);
addRead(propExtra, propExtra, propBinding);
setBindingDownstream(propBinding, propExtra);
}
}
} else if (spreadReferenceNodes) {
setBindingDownstream(
seen.size
? createBinding(
generateUid(`${getTagName(tag)}_attrs`),
propTree.binding.type,
propTree.binding.section,
propTree.binding,
undefined,
fromIter(seen),
spreadReferenceNodes[0].loc,
true,
)
: propTree.binding,
(inputExpr.value = mergeReferences(
section,
tag.node,
spreadReferenceNodes,
)),
);
}
if (propTree.rest) {
const extra = (inputExpr.value = mergeReferences(
section,
tag.node,
unknownReferences.flat(),
));
setBindingDownstream(propTree.binding, extra);
setBindingDownstream(
propTree.binding,
(inputExpr.value = mergeReferences(
section,
tag.node,
unknownReferences.flat(),
)),
);
} else {
unknownReferences.forEach(dropReferences);
}
@ -671,6 +699,22 @@ function analyzeAttrs(
return inputExpr;
}
function getSingleSpreadBinding(
attributes: (t.MarkoAttribute | t.MarkoSpreadAttribute)[],
) {
let binding: Binding | undefined;
for (let i = attributes.length; i--; ) {
const attr = attributes[i];
if (
attr.type === "MarkoSpreadAttribute" &&
(binding || !(binding = attr.value.extra?.spreadFrom))
) {
return;
}
}
return binding;
}
function writeParamsToSignals(
tag: t.NodePath<t.MarkoTag>,
propTree: BindingPropTree,
@ -927,26 +971,36 @@ function writeAttrsToSignals(
}
}
const { attributes } = tag.node;
const staticAttrs: t.MarkoAttribute[] = [];
let knownSpreadBinding: Binding | undefined;
let spreadProps: t.ObjectExpression["properties"] | undefined;
const staticAttrs: t.MarkoAttribute[] = [];
const knownSpreadDefaultAttrs = new Set<t.MarkoAttribute>();
const { attributes } = tag.node;
for (let i = attributes.length; i--; ) {
const attr = attributes[i];
if (t.isMarkoAttribute(attr)) {
const childAttrExports = propTree.props[attr.name];
if (!childAttrExports || seen.has(attr.name)) continue;
if (spreadProps) {
spreadProps.push(toObjectProperty(attr.name, attr.value));
if (
!propTree.props[attr.name] ||
seen.has(attr.name) ||
spreadProps?.push(toObjectProperty(attr.name, attr.value))
) {
continue;
}
seen.add(attr.name);
staticAttrs.push(attr);
if (knownSpreadBinding) {
knownSpreadDefaultAttrs.add(attr);
}
} else if (spreadProps) {
spreadProps.push(t.spreadElement(attr.value));
} else {
spreadProps = [t.spreadElement(attr.value)];
if (!propTree.rest) {
knownSpreadBinding = getSingleSpreadBinding(attributes);
}
if (!knownSpreadBinding) {
spreadProps = [t.spreadElement(attr.value)];
}
}
}
@ -963,7 +1017,16 @@ function writeAttrsToSignals(
t.expressionStatement(
t.callExpression(attrExportIdentifier, [
createScopeReadExpression(info.childScopeBinding, info.tagSection),
attr.value,
knownSpreadDefaultAttrs.has(attr)
? t.logicalExpression(
"??",
createScopeReadExpression(
knownSpreadBinding!.propertyAliases.get(attr.name)!,
info.tagSection,
),
attr.value,
)
: attr.value,
]),
),
);
@ -975,7 +1038,30 @@ function writeAttrsToSignals(
if (missing.size) {
const referencedBindings = tag.node.extra?.referencedBindings;
if (spreadProps) {
if (knownSpreadBinding) {
for (const prop of missing) {
const childAttrExports = propTree.props[prop];
const attrExportIdentifier = info.getBindingIdentifier(
childAttrExports.binding,
`${importAlias}_${prop}`,
);
const propBinding = knownSpreadBinding!.propertyAliases.get(prop)!;
addStatement(
"render",
info.tagSection,
propBinding,
t.expressionStatement(
t.callExpression(attrExportIdentifier, [
createScopeReadExpression(
info.childScopeBinding,
info.tagSection,
),
createScopeReadExpression(propBinding, info.tagSection),
]),
),
);
}
} else if (spreadProps) {
const spreadExpr = propsToExpression(
propTree.rest
? (translatedAttrs = translateAttrs(tag, propTree, seen)).properties

View File

@ -116,11 +116,7 @@ export type Intersection = Many<Binding>;
interface ReferencedFunctionExtra extends t.FunctionExtra, ReferencedExtra {}
interface Read {
binding: Binding;
node:
| undefined
| t.Identifier
| t.MemberExpression
| t.OptionalMemberExpression;
extra: t.NodeExtra | undefined;
}
declare module "@marko/compiler/dist/types" {
@ -134,6 +130,7 @@ declare module "@marko/compiler/dist/types" {
read?: { binding: Binding; props: Opt<string> };
pruned?: true;
isEffect?: true;
spreadFrom?: Binding;
[kIsInvoked]?: true;
}
@ -206,6 +203,19 @@ export function createBinding(
return binding;
}
export function getOrCreatePropertyAlias(binding: Binding, property: string) {
return (
binding.propertyAliases.get(property) ||
createBinding(
`${binding.name}_${property.replace(/[^a-zA-Z0-9_$]/g, "_")}`,
binding.type,
binding.section,
binding,
property,
)
);
}
export function trackDomVarReferences(
tag: t.NodePath<t.MarkoTag>,
binding: Binding,
@ -694,7 +704,6 @@ function trackReference(
| t.NodePath<t.MemberExpression>
| t.NodePath<t.OptionalMemberExpression> = referencePath;
let reference = binding;
let propPath = binding.name;
while (true) {
const { parent } = root;
@ -712,15 +721,6 @@ function trackReference(
reference = reference.upstreamAlias;
}
if (reference.propertyAliases.has(prop)) {
root = root.parentPath as
| t.NodePath<t.MemberExpression>
| t.NodePath<t.OptionalMemberExpression>;
reference = reference.propertyAliases.get(prop)!;
propPath = reference.name;
continue;
}
if (isInvokedFunction(root.parentPath) && !isEventOrChangeHandler(prop)) {
break;
}
@ -728,13 +728,8 @@ function trackReference(
root = root.parentPath as
| t.NodePath<t.MemberExpression>
| t.NodePath<t.OptionalMemberExpression>;
reference = createBinding(
(propPath += `_${prop.replace(/[^a-zA-Z0-9_$]/g, "_")}`),
reference.type,
reference.section,
reference,
prop,
);
reference = getOrCreatePropertyAlias(reference, prop);
}
addReadToExpression(root, reference);
@ -1366,6 +1361,27 @@ const [getFunctionReadsByExpression] = createProgramState(
() => new Map<ReferencedExtra, Map<ReferencedFunctionExtra, OneMany<Read>>>(),
);
export function addRead(
exprExtra: ReferencedExtra,
readExtra: t.NodeExtra | undefined,
binding: Binding,
) {
const readsByExpression = getReadsByExpression();
const read: Read = {
binding,
extra: readExtra,
};
readsByExpression.set(
exprExtra,
push(readsByExpression.get(exprExtra), read),
);
return read;
}
export function dropRead(exprExtra: ReferencedExtra) {
getReadsByExpression().delete(exprExtra);
}
function addReadToExpression(
root:
| t.NodePath<t.Identifier>
@ -1377,13 +1393,12 @@ function addReadToExpression(
const fnRoot = getFnRoot(root);
const exprRoot = getExprRoot(fnRoot || root);
const exprExtra = (exprRoot.node.extra ??= {}) as ReferencedExtra;
const readsByExpression = getReadsByExpression();
const section = (exprExtra.section = getOrCreateSection(exprRoot));
const read: Read = { binding, node };
readsByExpression.set(
exprExtra,
push(readsByExpression.get(exprExtra), read),
);
const read = addRead(exprExtra, (node.extra ??= {}), binding);
if (root.parent.type === "MarkoSpreadAttribute") {
exprExtra.spreadFrom = binding;
}
if (fnRoot) {
const fnReadsByExpr = getFunctionReadsByExpression();
@ -1777,8 +1792,8 @@ function resolveReferencedBindings(
const rootBindings = getRootBindings(reads);
for (const read of reads) {
let { binding } = read;
if (read.node) {
const readExtra = (read.node.extra ??= {});
const readExtra = read.extra;
if (readExtra) {
if (readExtra.assignmentTo !== binding) {
readExtra.section = expr.section;
({ binding } = readExtra.read ??= resolveExpressionReference(
@ -1790,10 +1805,9 @@ function resolveReferencedBindings(
referencedBindings = bindingUtil.add(referencedBindings, binding);
}
} else if (reads) {
if (reads.node) {
const readExtra = (reads.node.extra ??= {});
readExtra.section = expr.section;
readExtra.read = createRead(reads.binding, undefined);
if (reads.extra) {
reads.extra.section = expr.section;
reads.extra.read = createRead(reads.binding, undefined);
}
referencedBindings = reads.binding;
}