diff --git a/.sizes.json b/.sizes.json index 9fe66df7c..2b079b194 100644 --- a/.sizes.json +++ b/.sizes.json @@ -7,8 +7,8 @@ { "name": "*", "total": { - "min": 13393, - "brotli": 5228 + "min": 13478, + "brotli": 5270 } }, { diff --git a/packages/runtime-tags/src/common/types.ts b/packages/runtime-tags/src/common/types.ts index b7694e6ff..ed8388e50 100644 --- a/packages/runtime-tags/src/common/types.ts +++ b/packages/runtime-tags/src/common/types.ts @@ -50,7 +50,7 @@ export enum AccessorChar { LoopScopeArray = "!", LoopScopeMap = "(", LoopValue = ")", - PreviousAttributes = "~", + EventAttributes = "~", } export enum NodeType { diff --git a/packages/runtime-tags/src/dom.ts b/packages/runtime-tags/src/dom.ts index db205d9fb..1db54ce25 100644 --- a/packages/runtime-tags/src/dom.ts +++ b/packages/runtime-tags/src/dom.ts @@ -13,6 +13,7 @@ export { html, attr, attrs, + attrsEvents, classAttr, styleAttr, props, diff --git a/packages/runtime-tags/src/dom/dom.ts b/packages/runtime-tags/src/dom/dom.ts index fd60d7c38..bf5431d48 100644 --- a/packages/runtime-tags/src/dom/dom.ts +++ b/packages/runtime-tags/src/dom/dom.ts @@ -8,6 +8,8 @@ import { import { getAbortSignal } from "./abort-signal"; import { on } from "./event"; +const eventHandlerReg = /^on[A-Z-]/; + export function isDocumentFragment(node: Node): node is DocumentFragment { return node.nodeType === NodeType.DocumentFragment; } @@ -49,48 +51,48 @@ export function attrs( elementAccessor: Accessor, nextAttrs: Record, ) { - const prevAttrs: typeof nextAttrs = - scope[elementAccessor + AccessorChar.PreviousAttributes] || {}; const element = scope[elementAccessor] as Element; + let events: undefined | Record; - if (prevAttrs) { - for (const name in prevAttrs) { - if (!(nextAttrs && name in nextAttrs)) { - element.removeAttribute(name); - } + for (const { name } of element.attributes) { + if (!(nextAttrs && name in nextAttrs)) { + element.removeAttribute(name); } } + // https://jsperf.com/object-keys-vs-for-in-with-closure/194 for (const name in nextAttrs) { const value = nextAttrs[name]; - if (value !== prevAttrs[name]) { - switch (name) { - case "class": - classAttr(element, value); - break; - case "style": - styleAttr(element, value); - break; - case "renderBody": - break; - default: - if (/^on[A-Z-]/.test(name)) { - on( - element, - (name[2] === "-" - ? name.slice(3) - : name.slice(2).toLowerCase()) as any, - value as any, - ); - } else { - attr(element, name, value); - } - break; - } + switch (name) { + case "class": + classAttr(element, value); + break; + case "style": + styleAttr(element, value); + break; + case "renderBody": + break; + default: + if (eventHandlerReg.test(name)) { + (events ??= {})[ + name[2] === "-" ? name.slice(3) : name.slice(2).toLowerCase() + ] = value; + } else { + attr(element, name, value); + } + break; } } - scope[elementAccessor + AccessorChar.PreviousAttributes] = nextAttrs; + scope[elementAccessor + AccessorChar.EventAttributes] = events; +} + +export function attrsEvents(scope: Scope, elementAccessor: Accessor) { + const element = scope[elementAccessor] as Element; + const events = scope[elementAccessor + AccessorChar.EventAttributes]; + for (const name in events) { + on(element, name as any, events[name] as any); + } } const doc = document; diff --git a/packages/runtime-tags/src/html/attrs.ts b/packages/runtime-tags/src/html/attrs.ts index be8b46fe1..bb99bee6c 100644 --- a/packages/runtime-tags/src/html/attrs.ts +++ b/packages/runtime-tags/src/html/attrs.ts @@ -1,4 +1,6 @@ import { classValue, isVoid, styleValue } from "../common/helpers"; +import { AccessorChar, type Accessor } from "../common/types"; +import { ensureScopeWithId } from "./writer"; export function classAttr(val: unknown) { return stringAttr("class", classValue(val)); @@ -12,8 +14,13 @@ export function attr(name: string, val: unknown) { return isVoid(val) ? "" : nonVoidAttr(name, val); } -export function attrs(data: Record) { +export function attrs( + data: Record, + elementAccessor?: Accessor, + scopeId?: number, +) { let result = ""; + let events: undefined | Record; for (const name in data) { const val = data[name]; @@ -29,13 +36,25 @@ export function attrs(data: Record) { // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 // Avoids outputting attribute names that would be invalid or // be an event handler / renderBody. - if (!(isVoid(val) || /^on[A-Z-]|^renderBody$|[\s/>"'=]/.test(name))) { - result += nonVoidAttr(name, val); + if (!isVoid(val)) { + if (/^on[A-Z-]/.test(name)) { + (events ??= {})[ + name[2] === "-" ? name.slice(3) : name.slice(2).toLowerCase() + ] = val; + } else if (!/^renderBody$|[\s/>"'=]/.test(name)) { + result += nonVoidAttr(name, val); + } } break; } } + if (events && elementAccessor) { + ensureScopeWithId(scopeId!)[ + (elementAccessor + AccessorChar.EventAttributes) as any + ] = events; + } + return result; } diff --git a/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/csr-sanitized.expected.md b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/csr-sanitized.expected.md new file mode 100644 index 000000000..cf47116d6 --- /dev/null +++ b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/csr-sanitized.expected.md @@ -0,0 +1,36 @@ +# Render {} +```html + +``` + + +# Render +container.querySelector("button").click() + +```html + +``` + + +# Render +container.querySelector("button").click() + +```html + +``` + + +# Render +container.querySelector("button").click() + +```html + +``` \ No newline at end of file diff --git a/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/csr.expected.md b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/csr.expected.md new file mode 100644 index 000000000..62019ae39 --- /dev/null +++ b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/csr.expected.md @@ -0,0 +1,56 @@ +# Render {} +```html + +``` + +# Mutations +``` +inserted button0 +``` + + +# Render +container.querySelector("button").click() + +```html + +``` + +# Mutations +``` +button0/#text0: "0" => "1" +``` + + +# Render +container.querySelector("button").click() + +```html + +``` + +# Mutations +``` +button0/#text0: "1" => "2" +``` + + +# Render +container.querySelector("button").click() + +```html + +``` + +# Mutations +``` +button0/#text0: "2" => "3" +``` \ No newline at end of file diff --git a/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/dom.expected/components/FancyButton.js b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/dom.expected/components/FancyButton.js new file mode 100644 index 000000000..3b2dd4a6f --- /dev/null +++ b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/dom.expected/components/FancyButton.js @@ -0,0 +1,27 @@ +import { attrs as _attrs2, attrsEvents as _attrsEvents, conditional as _conditional, register as _register, queueEffect as _queueEffect, value as _value, createRenderer as _createRenderer, createTemplate as _createTemplate } from "@marko/runtime-tags/debug/dom"; +const _dynamicTagName = /* @__PURE__ */_conditional("#text/1"); +const _attrs_effect = _register("packages/translator-tags/src/__tests__/fixtures/body-content-new/components/FancyButton.marko_0_attrs", _scope => _attrsEvents(_scope, "#button/0")); +const _attrs = /* @__PURE__ */_value("attrs", (_scope, attrs) => { + _attrs2(_scope, "#button/0", attrs); + _queueEffect(_scope, _attrs_effect); +}); +const _renderBody = /* @__PURE__ */_value("renderBody", (_scope, renderBody) => _dynamicTagName(_scope, renderBody), void 0, _dynamicTagName); +const _destructure2 = (_scope, _destructure, _clean) => { + let renderBody, attrs; + if (!_clean) ({ + renderBody, + ...attrs + } = _destructure); + _renderBody(_scope, renderBody, _clean); + _attrs(_scope, attrs, _clean); +}; +const _input = /* @__PURE__ */_value("input", (_scope, input) => _destructure2(_scope, input), void 0, _destructure2); +export const _args_ = (_scope, _destructure3, _clean) => { + let input; + if (!_clean) [input] = _destructure3; + _input(_scope, input, _clean); +}; +export const _template_ = ""; +export const _walks_ = /* get, next(1), replace, out(1) */" D%l"; +export const _setup_ = function () {}; +export default /* @__PURE__ */_createTemplate( /* @__PURE__ */_createRenderer(_template_, _walks_, _setup_, void 0, void 0, _args_), "packages/translator-tags/src/__tests__/fixtures/body-content-new/components/FancyButton.marko"); \ No newline at end of file diff --git a/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/dom.expected/template.hydrate.js b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/dom.expected/template.hydrate.js new file mode 100644 index 000000000..2fe489665 --- /dev/null +++ b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/dom.expected/template.hydrate.js @@ -0,0 +1,4 @@ +import { init } from "@marko/runtime-tags/debug/dom"; +import "./template.marko"; +import "./components/FancyButton.marko"; +init(); \ No newline at end of file diff --git a/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/dom.expected/template.js b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/dom.expected/template.js new file mode 100644 index 000000000..e3fe3fda6 --- /dev/null +++ b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/dom.expected/template.js @@ -0,0 +1,24 @@ +import { queueSource as _queueSource, data as _data, register as _register, bindRenderer as _bindRenderer, inChild as _inChild, dynamicClosure as _dynamicClosure, registerSubscriber as _registerSubscriber, createRenderer as _createRenderer, dynamicSubscribers as _dynamicSubscribers, value as _value, createTemplate as _createTemplate } from "@marko/runtime-tags/debug/dom"; +import { _setup_ as _FancyButton, _args_ as _FancyButton_args, _template_ as _FancyButton_template, _walks_ as _FancyButton_walks } from "./components/FancyButton.marko"; +const _onClick = _register("packages/translator-tags/src/__tests__/fixtures/body-content-new/template.marko_0/onClick", _scope => { + const { + clickCount + } = _scope; + return function () { + _queueSource(_scope, _clickCount, clickCount + 1); + }; +}); +const _clickCount$FancyButtonBody = _registerSubscriber("packages/translator-tags/src/__tests__/fixtures/body-content-new/template.marko_1_clickCount/subscriber", /* @__PURE__ */_dynamicClosure("clickCount", (_scope, clickCount) => _data(_scope["#text/0"], clickCount))); +const _FancyButtonBody = _register("packages/translator-tags/src/__tests__/fixtures/body-content-new/template.marko_1_renderer", /* @__PURE__ */_createRenderer(" ", /* get */" ", void 0, [_clickCount$FancyButtonBody])); +const _clickCount = /* @__PURE__ */_value("clickCount", (_scope, clickCount) => _FancyButton_args(_scope["#childScope/0"], [{ + onClick: _onClick(_scope), + renderBody: /* @__PURE__ */_bindRenderer(_scope, _FancyButtonBody) +}]), _dynamicSubscribers("clickCount"), _inChild("#childScope/0", _FancyButton_args)); +const _setup = _scope => { + _FancyButton(_scope["#childScope/0"]); + _clickCount(_scope, 0); +}; +export const _template_ = `${_FancyButton_template}`; +export const _walks_ = /* beginChild, _FancyButton_walks, endChild */`/${_FancyButton_walks}&`; +export const _setup_ = _setup; +export default /* @__PURE__ */_createTemplate( /* @__PURE__ */_createRenderer(_template_, _walks_, _setup_), "packages/translator-tags/src/__tests__/fixtures/body-content-new/template.marko"); \ No newline at end of file diff --git a/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/html.expected/components/FancyButton.js b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/html.expected/components/FancyButton.js new file mode 100644 index 000000000..930fe1662 --- /dev/null +++ b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/html.expected/components/FancyButton.js @@ -0,0 +1,18 @@ +import { attrs as _attrs2, write as _write, dynamicTagInput as _dynamicTagInput, markResumeControlEnd as _markResumeControlEnd, markResumeNode as _markResumeNode, writeEffect as _writeEffect, writeScope as _writeScope, nextScopeId as _nextScopeId, createRenderer as _createRenderer, createTemplate as _createTemplate } from "@marko/runtime-tags/debug/html"; +const _renderer = /* @__PURE__ */_createRenderer((input, _tagVar) => { + const _scope0_id = _nextScopeId(); + const { + renderBody, + ...attrs + } = input; + _write(``); + const _dynamicScope = _dynamicTagInput(renderBody, {}); + _write(`${_markResumeControlEnd(_scope0_id, "#text/1")}${_markResumeNode(_scope0_id, "#button/0")}`); + _writeEffect(_scope0_id, "packages/translator-tags/src/__tests__/fixtures/body-content-new/components/FancyButton.marko_0_attrs"); + _writeScope(_scope0_id, { + "attrs": attrs, + "#text/1!": _dynamicScope, + "#text/1(": renderBody + }); +}); +export default /* @__PURE__ */_createTemplate(_renderer, "packages/translator-tags/src/__tests__/fixtures/body-content-new/components/FancyButton.marko"); \ No newline at end of file diff --git a/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/html.expected/template.js b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/html.expected/template.js new file mode 100644 index 000000000..973e1124b --- /dev/null +++ b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/html.expected/template.js @@ -0,0 +1,25 @@ +import { escapeXML as _escapeXML, markResumeNode as _markResumeNode, write as _write, ensureScopeWithId as _ensureScopeWithId, writeEffect as _writeEffect, writeScope as _writeScope, nextScopeId as _nextScopeId, register as _register, createRenderer as _createRenderer, peekNextScope as _peekNextScope, createTemplate as _createTemplate } from "@marko/runtime-tags/debug/html"; +import _FancyButton from "./components/FancyButton.marko"; +const _renderer = /* @__PURE__ */_createRenderer((input, _tagVar) => { + const _scope0_id = _nextScopeId(); + const clickCount = 0; + const _childScope = _peekNextScope(); + _FancyButton._({ + onClick: _register(function () { + clickCount++; + }, "packages/translator-tags/src/__tests__/fixtures/body-content-new/template.marko_0/onClick", _scope0_id), + renderBody: /* @__PURE__ */_createRenderer(() => { + const _scope1_id = _nextScopeId(); + _write(`${_escapeXML(clickCount)}${_markResumeNode(_scope1_id, "#text/0")}`); + _writeEffect(_scope1_id, "packages/translator-tags/src/__tests__/fixtures/body-content-new/template.marko_1_clickCount/subscriber"); + _writeScope(_scope1_id, { + "_": _ensureScopeWithId(_scope0_id) + }); + }) + }); + _writeScope(_scope0_id, { + "clickCount": clickCount, + "#childScope/0": _childScope + }); +}); +export default /* @__PURE__ */_createTemplate(_renderer, "packages/translator-tags/src/__tests__/fixtures/body-content-new/template.marko"); \ No newline at end of file diff --git a/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/resume-sanitized.expected.md b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/resume-sanitized.expected.md new file mode 100644 index 000000000..cf47116d6 --- /dev/null +++ b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/resume-sanitized.expected.md @@ -0,0 +1,36 @@ +# Render {} +```html + +``` + + +# Render +container.querySelector("button").click() + +```html + +``` + + +# Render +container.querySelector("button").click() + +```html + +``` + + +# Render +container.querySelector("button").click() + +```html + +``` \ No newline at end of file diff --git a/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/resume.expected.md b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/resume.expected.md new file mode 100644 index 000000000..b63891307 --- /dev/null +++ b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/resume.expected.md @@ -0,0 +1,102 @@ +# Render {} +```html + + + + + + + + +``` + +# Mutations +``` + +``` + + +# Render +container.querySelector("button").click() + +```html + + + + + + + + +``` + +# Mutations +``` +inserted #document/html0/body1/button0/#text0 +removed #comment after #document/html0/body1/button0/#text0 +removed #text after #document/html0/body1/button0/#text0 +removed #comment after #document/html0/body1/button0/#text0 +#document/html0/body1/button0/#text0: " " => "1" +``` + + +# Render +container.querySelector("button").click() + +```html + + + + + + + + +``` + +# Mutations +``` +#document/html0/body1/button0/#text0: "1" => "2" +``` + + +# Render +container.querySelector("button").click() + +```html + + + + + + + + +``` + +# Mutations +``` +#document/html0/body1/button0/#text0: "2" => "3" +``` \ No newline at end of file diff --git a/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/ssr-sanitized.expected.md b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/ssr-sanitized.expected.md new file mode 100644 index 000000000..28c35d3a5 --- /dev/null +++ b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/ssr-sanitized.expected.md @@ -0,0 +1,6 @@ +# Render "End" +```html + +``` \ No newline at end of file diff --git a/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/ssr.expected.md b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/ssr.expected.md new file mode 100644 index 000000000..7b572ab1e --- /dev/null +++ b/packages/translator-tags/src/__tests__/fixtures/body-content-new/__snapshots__/ssr.expected.md @@ -0,0 +1,37 @@ +# Write + + + +# Render "End" +```html + + + + + + + + +``` + +# Mutations +``` +inserted #document/html0 +inserted #document/html0/head0 +inserted #document/html0/body1 +inserted #document/html0/body1/button0 +inserted #document/html0/body1/button0/#comment0 +inserted #document/html0/body1/button0/#text1 +inserted #document/html0/body1/button0/#comment2 +inserted #document/html0/body1/button0/#comment3 +inserted #document/html0/body1/#comment1 +inserted #document/html0/body1/script2 +inserted #document/html0/body1/script2/#text0 +``` \ No newline at end of file diff --git a/packages/translator-tags/src/__tests__/fixtures/body-content-new/components/FancyButton.marko b/packages/translator-tags/src/__tests__/fixtures/body-content-new/components/FancyButton.marko new file mode 100644 index 000000000..66f2195f1 --- /dev/null +++ b/packages/translator-tags/src/__tests__/fixtures/body-content-new/components/FancyButton.marko @@ -0,0 +1,2 @@ + + diff --git a/packages/translator-tags/src/__tests__/fixtures/body-content-new/template.marko b/packages/translator-tags/src/__tests__/fixtures/body-content-new/template.marko new file mode 100644 index 000000000..a5a65bb4f --- /dev/null +++ b/packages/translator-tags/src/__tests__/fixtures/body-content-new/template.marko @@ -0,0 +1,2 @@ + +${clickCount} diff --git a/packages/translator-tags/src/__tests__/fixtures/body-content-new/test.ts b/packages/translator-tags/src/__tests__/fixtures/body-content-new/test.ts new file mode 100644 index 000000000..0fd2af27d --- /dev/null +++ b/packages/translator-tags/src/__tests__/fixtures/body-content-new/test.ts @@ -0,0 +1,5 @@ +const click = (container: Element) => { + container.querySelector("button")!.click(); +}; + +export const steps = [{}, click, click, click]; diff --git a/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/csr-sanitized.expected.md b/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/csr-sanitized.expected.md index 9b07541c1..ba2cdab52 100644 --- a/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/csr-sanitized.expected.md +++ b/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/csr-sanitized.expected.md @@ -36,12 +36,16 @@ # Render {"value":{}} ```html -
+
``` diff --git a/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/csr.expected.md b/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/csr.expected.md index 130ce885c..63302886e 100644 --- a/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/csr.expected.md +++ b/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/csr.expected.md @@ -41,32 +41,39 @@ inserted div0, div1, div2 # Mutations ``` div0: attr(a) "1" => null +div0: attr(b) "2" => "2" div0: attr(c) null => "3" div1: attr(a) "1" => "0" +div1: attr(b) "2" => "2" div1: attr(c) null => "3" +div2: attr(b) "2" => "2" div2: attr(c) null => "3" +div2: attr(a) "0" => "0" ``` # Render {"value":{}} ```html -
+
``` # Mutations ``` div0: attr(b) "2" => null -div0: attr(c) "3" => null div1: attr(b) "2" => null -div1: attr(c) "3" => null +div1: attr(a) "0" => "0" div2: attr(b) "2" => null -div2: attr(c) "3" => null +div2: attr(a) "0" => "0" ``` @@ -83,7 +90,11 @@ div2: attr(c) "3" => null # Mutations ``` - +div0: attr(c) "3" => null +div1: attr(c) "3" => null +div1: attr(a) "0" => "0" +div2: attr(c) "3" => null +div2: attr(a) "0" => "0" ``` @@ -104,4 +115,5 @@ div2: attr(c) "3" => null ``` div0: attr(a) null => "1" div1: attr(a) "0" => "1" +div2: attr(a) "0" => "0" ``` \ No newline at end of file diff --git a/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/dom.expected/template.js b/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/dom.expected/template.js index 793b1e3f8..3e109fc5f 100644 --- a/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/dom.expected/template.js +++ b/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/dom.expected/template.js @@ -1,4 +1,8 @@ -import { attrs as _attrs, intersection as _intersection, value as _value, createRenderer as _createRenderer, createTemplate as _createTemplate } from "@marko/runtime-tags/debug/dom"; +import { attrs as _attrs, attrsEvents as _attrsEvents, register as _register, queueEffect as _queueEffect, intersection as _intersection, value as _value, createRenderer as _createRenderer, createTemplate as _createTemplate } from "@marko/runtime-tags/debug/dom"; +const _expr_input_a_effect = _register("packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/template.marko_0_input_a", _scope => { + _attrsEvents(_scope, "#div/1"); + _attrsEvents(_scope, "#div/2"); +}); const _expr_input_a = /* @__PURE__ */_intersection(2, _scope => { const { input, @@ -12,9 +16,14 @@ const _expr_input_a = /* @__PURE__ */_intersection(2, _scope => { ...input.value, a: a }); + _queueEffect(_scope, _expr_input_a_effect); }); const _a = /* @__PURE__ */_value("a", null, _expr_input_a); -const _input = /* @__PURE__ */_value("input", (_scope, input) => _attrs(_scope, "#div/0", input.value), _expr_input_a); +const _input_effect = _register("packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/template.marko_0_input", _scope => _attrsEvents(_scope, "#div/0")); +const _input = /* @__PURE__ */_value("input", (_scope, input) => { + _attrs(_scope, "#div/0", input.value); + _queueEffect(_scope, _input_effect); +}, _expr_input_a); const _setup = _scope => { _a(_scope, 0); }; diff --git a/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/html.expected/template.js b/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/html.expected/template.js index c094f7e42..382c73e08 100644 --- a/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/html.expected/template.js +++ b/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/html.expected/template.js @@ -1,14 +1,16 @@ -import { attrs as _attrs, markResumeNode as _markResumeNode, write as _write, writeScope as _writeScope, nextScopeId as _nextScopeId, createRenderer as _createRenderer, createTemplate as _createTemplate } from "@marko/runtime-tags/debug/html"; +import { attrs as _attrs, markResumeNode as _markResumeNode, write as _write, writeEffect as _writeEffect, writeScope as _writeScope, nextScopeId as _nextScopeId, createRenderer as _createRenderer, createTemplate as _createTemplate } from "@marko/runtime-tags/debug/html"; const _renderer = /* @__PURE__ */_createRenderer((input, _tagVar) => { const _scope0_id = _nextScopeId(); const a = 0; - _write(`
${_markResumeNode(_scope0_id, "#div/0")}
${_markResumeNode(_scope0_id, "#div/0")}
${_markResumeNode(_scope0_id, "#div/1")}
${_markResumeNode(_scope0_id, "#div/1")}
${_markResumeNode(_scope0_id, "#div/2")}`); + }, "#div/2", _scope0_id)}>
${_markResumeNode(_scope0_id, "#div/2")}`); + _writeEffect(_scope0_id, "packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/template.marko_0_input_a"); + _writeEffect(_scope0_id, "packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/template.marko_0_input"); _writeScope(_scope0_id, { "input": input, "a": a diff --git a/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/resume.expected.md b/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/resume.expected.md index 665d999b6..a570efe92 100644 --- a/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/resume.expected.md +++ b/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/resume.expected.md @@ -19,7 +19,7 @@ /> diff --git a/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/ssr.expected.md b/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/ssr.expected.md index 7f536a792..2478b1026 100644 --- a/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/ssr.expected.md +++ b/packages/translator-tags/src/__tests__/fixtures/update-dynamic-attrs/__snapshots__/ssr.expected.md @@ -1,5 +1,5 @@ # Write -
+
# Render "End" @@ -23,7 +23,7 @@ /> diff --git a/packages/translator-tags/src/visitors/tag/native-tag.ts b/packages/translator-tags/src/visitors/tag/native-tag.ts index 970aaa3e8..c7c7762a9 100644 --- a/packages/translator-tags/src/visitors/tag/native-tag.ts +++ b/packages/translator-tags/src/visitors/tag/native-tag.ts @@ -17,7 +17,11 @@ import { createScopeReadExpression, getScopeExpression, } from "../../util/scope-read"; -import { getOrCreateSection, getSection } from "../../util/sections"; +import { + getOrCreateSection, + getScopeIdIdentifier, + getSection, +} from "../../util/sections"; import { addHTMLEffectCall, addStatement } from "../../util/signals"; import translateVar from "../../util/translate-var"; import * as walks from "../../util/walks"; @@ -168,21 +172,27 @@ export default { write`<${name.node}`; if (hasSpread) { + const attrsObj = attrsToObject(tag)!; if (isHTML) { - write`${callRuntime("attrs", attrsToObject(tag)!)}`; + addHTMLEffectCall(section, extra.referencedBindings); + write`${callRuntime("attrs", attrsObj, visitAccessor, getScopeIdIdentifier(section))}`; } else { addStatement( "render", section, extra.referencedBindings, t.expressionStatement( - callRuntime( - "attrs", - scopeIdentifier, - visitAccessor, - attrsToObject(tag)!, - ), + callRuntime("attrs", scopeIdentifier, visitAccessor, attrsObj), + ), + ); + addStatement( + "effect", + section, + extra.referencedBindings, + t.expressionStatement( + callRuntime("attrsEvents", scopeIdentifier, visitAccessor), ), + attrsObj, ); } } else {