diff --git a/packages/translator/src/__tests__/fixtures/basic-counter/__snapshots__/csr.expected.md b/packages/translator/src/__tests__/fixtures/basic-counter/__snapshots__/csr.expected.md index e0b9e7814..91eb9edda 100644 --- a/packages/translator/src/__tests__/fixtures/basic-counter/__snapshots__/csr.expected.md +++ b/packages/translator/src/__tests__/fixtures/basic-counter/__snapshots__/csr.expected.md @@ -19,7 +19,24 @@ container.querySelector("button").click(); ```html
+
+``` + +# Mutations +``` +div0/button0/#text0: "0" => "1" +``` + + +# Render +container.querySelector("button").click(); + +```html +
+
``` @@ -36,24 +53,7 @@ container.querySelector("button").click(); ```html
-
-``` - -# Mutations -``` - -``` - - -# Render -container.querySelector("button").click(); - -```html -
-
``` diff --git a/packages/translator/src/__tests__/fixtures/basic-counter/__snapshots__/dom.expected.js b/packages/translator/src/__tests__/fixtures/basic-counter/__snapshots__/dom.expected.js index df20c8807..24b1e7f81 100644 --- a/packages/translator/src/__tests__/fixtures/basic-counter/__snapshots__/dom.expected.js +++ b/packages/translator/src/__tests__/fixtures/basic-counter/__snapshots__/dom.expected.js @@ -1,4 +1,4 @@ -import { write as _write, read as _read, on as _on, data as _data, bind as _bind, createRenderFn as _createRenderFn } from "@marko/runtime-fluurt/src/dom"; +import { queue as _queue, write as _write, read as _read, on as _on, data as _data, bind as _bind, createRenderFn as _createRenderFn } from "@marko/runtime-fluurt/src/dom"; function _apply() { _apply_clickCount(0); @@ -7,7 +7,7 @@ function _apply() { const _onclick = function () { const clickCount = _read(2); - clickCount++; + _queue(_apply_clickCount, 2, clickCount + 1); }; function _apply_clickCount(clickCount) { diff --git a/packages/translator/src/__tests__/fixtures/basic-counter/template.marko b/packages/translator/src/__tests__/fixtures/basic-counter/template.marko index f2d831ef0..369d501a2 100644 --- a/packages/translator/src/__tests__/fixtures/basic-counter/template.marko +++ b/packages/translator/src/__tests__/fixtures/basic-counter/template.marko @@ -1,4 +1,6 @@
- +
diff --git a/packages/translator/src/core/translate-const.ts b/packages/translator/src/core/translate-const.ts index f67fda58b..d80546944 100644 --- a/packages/translator/src/core/translate-const.ts +++ b/packages/translator/src/core/translate-const.ts @@ -47,7 +47,7 @@ export default function enter(tag: t.NodePath) { identifiers.length === 1 ? t.expressionStatement( t.callExpression( - writer.getApplyId(tag, identifiers[0].extra.binding!), + writer.bindingToApplyId(tag, identifiers[0].extra.binding!), [defaultAttr.value] ) ) @@ -58,7 +58,7 @@ export default function enter(tag: t.NodePath) { ...identifiers.map((identifier) => t.expressionStatement( t.callExpression( - writer.getApplyId(tag, identifier.extra.binding!), + writer.bindingToApplyId(tag, identifier.extra.binding!), [t.identifier(identifier.name)] ) ) diff --git a/packages/translator/src/core/translate-let.ts b/packages/translator/src/core/translate-let.ts index 75b20a7c6..77e4f54ac 100644 --- a/packages/translator/src/core/translate-let.ts +++ b/packages/translator/src/core/translate-let.ts @@ -4,21 +4,24 @@ import { assertNoBodyContent } from "../util/assert"; import translateVar from "../util/translate-var"; import { isOutputDOM } from "../util/marko-config"; import * as writer from "../util/writer"; +import { callRuntime } from "../util/runtime"; +import replaceAssignments from "../util/replace-assignments"; export default function enter(tag: t.NodePath) { const { node } = tag; + const tagVar = node.var; const [defaultAttr] = node.attributes; assertNoParams(tag); assertNoBodyContent(tag); - if (!node.var) { + if (!tagVar) { throw tag .get("name") .buildCodeFrameError("The 'let' tag requires a tag variable."); } - if (!t.isIdentifier(node.var)) { + if (!t.isIdentifier(tagVar)) { throw tag .get("var") .buildCodeFrameError("The 'let' cannot be destructured."); @@ -43,16 +46,19 @@ export default function enter(tag: t.NodePath) { } if (isOutputDOM(tag)) { + const binding = tagVar.extra.binding!; + const applyId = writer.bindingToApplyId(tag, binding); + const scopeId = writer.bindingToScopeId(tag, binding); // TODO: add defined guard if bindings exist. writer.addStatement( "apply", tag, defaultAttr.extra?.valueReferences, - t.expressionStatement( - t.callExpression(writer.getApplyId(tag, node.var.extra.binding!), [ - defaultAttr.value, - ]) - ) + t.expressionStatement(t.callExpression(applyId, [defaultAttr.value])) + ); + + replaceAssignments(tag.scope.getBinding(binding.name)!, (v) => + callRuntime(tag, "queue", applyId, scopeId, v) ); } else { translateVar(tag, defaultAttr.value); diff --git a/packages/translator/src/util/replace-assignments.ts b/packages/translator/src/util/replace-assignments.ts new file mode 100644 index 000000000..00f96c23d --- /dev/null +++ b/packages/translator/src/util/replace-assignments.ts @@ -0,0 +1,33 @@ +import { types as t } from "@marko/compiler"; + +export default function replaceAssignments( + binding: t.Binding, + map: (value: t.Expression) => t.Expression +): void { + for (const assignment of binding.constantViolations) { + let value: t.Expression | undefined; + if (assignment.isUpdateExpression()) { + value = t.binaryExpression( + assignment.node.operator === "++" ? "+" : "-", + binding.identifier, + t.numericLiteral(1) + ); + } else if (assignment.isAssignmentExpression()) { + value = + assignment.node.operator === "=" + ? assignment.node.right + : t.binaryExpression( + assignment.node.operator.slice( + 0, + -1 + ) as t.BinaryExpression["operator"], + binding.identifier, + assignment.node.right + ); + } + + if (value) { + assignment.parentPath!.replaceWith(map(value)); + } + } +} diff --git a/packages/translator/src/util/writer.ts b/packages/translator/src/util/writer.ts index 9b8cc4027..4eeec09ef 100644 --- a/packages/translator/src/util/writer.ts +++ b/packages/translator/src/util/writer.ts @@ -283,7 +283,7 @@ export function addStatement( path, "queue", identifier, - t.numericLiteral(bindingToScopeId(path, binding)) + bindingToScopeId(path, binding) ) ) ); @@ -300,7 +300,7 @@ export function addStatement( } } -export function getApplyId(path: t.NodePath, binding: Binding) { +export function bindingToApplyId(path: t.NodePath, binding: Binding) { const section = path.state.section as Section; const groupIndex = sorted.findIndex(compareReferenceGroups, section.apply, { references: [binding], @@ -332,11 +332,7 @@ function writeApplyGroups(path: t.NodePath, groups: ReferenceGroup[]) { params = references.map((binding) => t.assignmentPattern( t.identifier(binding.name), - callRuntime( - path, - "read", - t.numericLiteral(bindingToScopeId(path, binding)) - ) + callRuntime(path, "read", bindingToScopeId(path, binding)) ) ); body = t.blockStatement(statements); @@ -348,7 +344,7 @@ function writeApplyGroups(path: t.NodePath, groups: ReferenceGroup[]) { callRuntime( path, "write", - t.numericLiteral(bindingToScopeId(path, references)), + bindingToScopeId(path, references), param ), statements.length === 1 @@ -381,11 +377,7 @@ function writeHydrateGroups(path: t.NodePath, groups: ReferenceGroup[]) { ? (Array.isArray(references) ? references : [references]).map((binding) => t.assignmentPattern( t.identifier(binding.name), - callRuntime( - path, - "read", - t.numericLiteral(bindingToScopeId(path, binding)) - ) + callRuntime(path, "read", bindingToScopeId(path, binding)) ) ) : []; @@ -475,7 +467,7 @@ export function bindingToScopeId( path: t.NodePath, { sectionIndex, bindingIndex }: Binding ) { - return ( + return t.numericLiteral( path.hub.file.path.node.extra.sections![sectionIndex].visits + bindingIndex ); } @@ -536,11 +528,7 @@ function bindFunction( (Array.isArray(references) ? references : [references]).map((binding) => t.variableDeclarator( t.identifier(binding.name), - callRuntime( - fn, - "read", - t.numericLiteral(bindingToScopeId(fn, binding)) - ) + callRuntime(fn, "read", bindingToScopeId(fn, binding)) ) ) )