refactor: move referenceGroup creation into analyze

This commit is contained in:
Michael Rawlings 2022-04-08 23:52:06 -04:00
parent d72a0afa97
commit cd8f6b292c
29 changed files with 568 additions and 427 deletions

View File

@ -69,3 +69,9 @@ function toDelimitedString(
export function isVoid(value: unknown) {
return value == null || value === false;
}
export function alphaEncode(num: number): string {
return num < 52
? String.fromCharCode(num < 26 ? num + 97 : num + (65 - 26))
: alphaEncode((num / 52) | 0) + alphaEncode(num % 52);
}

View File

@ -3,7 +3,7 @@ let _item;
import { data as _data, write as _write, setConditionalRenderer as _setConditionalRenderer, createRenderer as _createRenderer, createRenderFn as _createRenderFn } from "@marko/runtime-fluurt/src/dom";
import { apply as _hello, template as _hello_template, walks as _hello_walks } from "./components/hello/index.marko";
function _apply(_scope) {
function _apply4(_scope) {
_data(_scope[0], y);
}
@ -11,7 +11,7 @@ function _apply1_x(_scope, x = _scope._[1]) {
_setConditionalRenderer(_scope, 0, x ? _if : null);
}
function _apply2(_scope) {
function _apply(_scope) {
_hello(_scope[0]);
}
@ -21,7 +21,7 @@ const _temp2 = _createRenderer("<!>",
_if = _createRenderer("", "", null),
_temp3 = _createRenderer("",
/* get */
" ", _apply);
" ", _apply4);
export const applyAttrs = function (_scope, {
x
@ -33,5 +33,5 @@ export const template = `${_hello_template}`;
export const walks =
/* beginChild(0), _hello_walks, endChild */
`/${_hello_walks}&`;
export const apply = _apply2;
export const apply = _apply;
export default _createRenderFn(template, walks, apply, applyAttrs);

View File

@ -1,6 +0,0 @@
packages/translator/src/__tests__/fixtures/attr-class/template.marko(12,4): Dynamic @tags cannot be mixed with body content.
10 |
11 | <${input.test} class=["a", { b: c, d }]>
> 12 | <@test class=["a", { b: c, d }]>Hello</@test>
| ^^^^^
13 | </>

View File

@ -1,15 +1,4 @@
_customTag({
class: ["a", {
b: c,
d
}]
});
_customTag({
class: ["a", false, "b"]
});
_dynamicTag(input.test, {
_dynamicTag(_scope, input.test, {
class: ["a", {
b: c,
d
@ -27,11 +16,18 @@ _dynamicTag(input.test, {
}
});
import { classAttr as _classAttr, queue as _queue, write as _write, dynamicTag as _dynamicTag, read as _read, createRenderFn as _createRenderFn } from "@marko/runtime-fluurt/src/dom";
import { hydrate as _customTag, template as _customTag_template, walks as _customTag_walks } from "./components/custom-tag.marko";
import { classAttr as _classAttr, write as _write, dynamicTag as _dynamicTag, queue as _queue, createRenderer as _createRenderer, createRenderFn as _createRenderFn } from "@marko/runtime-fluurt/src/dom";
import { apply as _customTag, template as _customTag_template, walks as _customTag_walks } from "./components/custom-tag.marko";
function _apply_input(input) {
if (_write(1, input)) {
function _applyWith_c_d(_scope, c = _scope[1], d = _scope[2]) {
_classAttr(_scope[0], ["a", {
b: c,
d
}]);
}
function _apply_input(_scope, input) {
if (_write(_scope, 5, input)) {
const {
c,
d
@ -43,22 +39,29 @@ function _apply_input(input) {
}
}
function _apply_c(c) {
if (_write(2, c)) _queue(_applyWith_d_c, 2);
function _apply_d(_scope, d) {
if (_write(_scope, 2, d)) _queue(_scope, _applyWith_c_d, 3);
}
function _apply_d(d) {
if (_write(3, d)) _queue(_applyWith_d_c, 3);
function _apply_c(_scope, c) {
if (_write(_scope, 1, c)) _queue(_scope, _applyWith_c_d, 3);
}
function _applyWith_d_c(d = _read(3), c = _read(2)) {
_classAttr(0, ["a", {
b: c,
d
}]);
function _apply(_scope) {
_customTag(_scope[3]);
_customTag(_scope[4]);
}
const _temp = _createRenderer("", "", null);
export const applyAttrs = function (_scope, input) {
_apply_input(_scope, input);
};
export { _apply_input };
export const template = `<div></div><div class="a b"></div><div class="a b c"></div>${_customTag_template}${_customTag_template}`;
export const walks = ` ${_customTag_walks}${_customTag_walks}`;
export const apply = null;
export default _createRenderFn(template, walks, apply);
export const walks =
/* get, over(3), beginChild(3), _customTag_walks, endChild, beginChild(4), _customTag_walks, endChild */
` d2${_customTag_walks}&3${_customTag_walks}&`;
export const apply = _apply;
export default _createRenderFn(template, walks, apply, applyAttrs);

View File

@ -25,7 +25,7 @@ function _apply_show(_scope, show) {
}
function _apply_message(_scope, message) {
if (_write(_scope, 5, message)) _queueInBranch(_scope, 1, _if, _apply1_message, 0, 1);
if (_write(_scope, 5, message)) _queueInBranch(_scope, 1, _if, _apply1_message, 1, 2);
}
function _apply(_scope) {

View File

@ -16,7 +16,7 @@ function _apply2_comment(_scope, comment = _scope._[8]) {
_queue(_scope, _apply2With_comment_id, 2);
}
function _apply2(_scope) {
function _apply3(_scope) {
_comments(_scope[0]);
_queue(_scope, _apply2_id, 1);
@ -54,7 +54,7 @@ function _apply1_id(_scope, id) {
if (_write(_scope, 10, id)) {
_attr(_scope[0], "id", id);
_queueInBranch(_scope, 4, _if, _apply2_id, 1, 3);
_queueInBranch(_scope, 4, _if, _apply2_id, 2, 4);
}
}
@ -68,7 +68,7 @@ function _apply1_comment(_scope, comment) {
_setConditionalRenderer(_scope, 4, comment.comments ? _if : null);
_queueInBranch(_scope, 4, _if, _apply2_comment, 0, 4);
_queueInBranch(_scope, 4, _if, _apply2_comment, 1, 5);
}
}
@ -76,24 +76,14 @@ function _apply1_path(_scope, path = _scope._[5]) {
_queue(_scope, _apply1With_path_i, 5);
}
function _apply1_path(_scope, path = _scope._[5]) {
_queue(_scope, _apply1With_path_i, 5);
}
function _apply(_scope) {
function _apply2(_scope) {
_apply1_open(_scope, true);
_queue(_scope, _apply1_path, 0);
_queue(_scope, _apply1_path, 0);
}
function _apply_path(_scope, path) {
if (_write(_scope, 5, path)) {
_queueForEach(_scope, 0, _apply1_path, 0, 6);
_queueForEach(_scope, 0, _apply1_path, 0, 7);
}
if (_write(_scope, 5, path)) _queueForEach(_scope, 0, _apply1_path, 1, 7);
}
function _apply_comments(_scope, comments) {
@ -102,10 +92,10 @@ function _apply_comments(_scope, comments) {
const _for = _createRenderer("<li><span> </span><button> </button><!></li>",
/* get, next(2), get, out(1), get, next(1), get, out(1), replace, skip(3) */
" E l D l%+", _apply),
" E l D l%+", _apply2),
_if = _createRenderer(`${_comments_template}`,
/* beginChild(0), _comments_walks, endChild */
`/${_comments_walks}&`, _apply2);
`/${_comments_walks}&`, _apply3);
export const applyAttrs = function (_scope, {
comments,

View File

@ -37,7 +37,7 @@ function _apply2(_scope) {
}
function _apply_selected(_scope, selected) {
if (_write(_scope, 4, selected)) _queueForEach(_scope, 0, _apply1_selected, 0, 3);
if (_write(_scope, 4, selected)) _queueForEach(_scope, 0, _apply1_selected, 1, 4);
}
function _apply(_scope) {

View File

@ -26,7 +26,7 @@ function _apply_clickCount(_scope, clickCount) {
if (_write(_scope, 4, clickCount)) {
_setConditionalRenderer(_scope, 0, clickCount < 3 ? _if : null);
_queueInBranch(_scope, 0, _if, _apply1_clickCount, 0, 1);
_queueInBranch(_scope, 0, _if, _apply1_clickCount, 1, 2);
}
}

View File

@ -94,10 +94,22 @@ function _apply_isLarge(_scope, isLarge) {
if (_write(_scope, 16, isLarge)) _apply_largeHeading(_scope, isLarge && "h1");
}
function _apply_showTagA(_scope, showTagA) {
if (_write(_scope, 15, showTagA)) {}
}
function _apply_show(_scope, show) {
if (_write(_scope, 14, show)) _apply_tagConstB(_scope, show ? "div" : null);
}
function _apply_x(_scope, x) {
if (_write(_scope, 13, x)) {}
}
function _apply_renderBody(_scope, renderBody) {
if (_write(_scope, 12, renderBody)) {}
}
function _apply_tagConstB(_scope, tagConstB) {
if (_write(_scope, 11, tagConstB)) {}
}

View File

@ -99,10 +99,6 @@ function _apply1_val(_scope, val) {
if (_write(_scope, 2, val)) _data(_scope[1], val);
}
function _apply_obj(_scope, obj) {
if (_write(_scope, 41, obj)) {}
}
function _apply_arr(_scope, arr) {
if (_write(_scope, 40, arr)) {
_setLoopOf(_scope, 0, arr, _for, null, _apply1_val);

View File

@ -1,6 +1,9 @@
import type { types as t } from "@marko/compiler";
import type { Tag } from "@marko/babel-utils";
import { trackReferencesForBindings } from "../util/references";
import {
getReferenceGroup,
trackReferencesForBindings,
} from "../util/references";
import { ReserveType } from "../util/reserve";
import { getOrCreateSectionId } from "../util/sections";
import { currentProgramPath } from "../visitors/program";
@ -22,14 +25,15 @@ export default {
string,
t.Identifier
>;
trackReferencesForBindings(
getOrCreateSectionId(tag),
varPath,
ReserveType.Attr
);
const sectionId = getOrCreateSectionId(tag);
trackReferencesForBindings(sectionId, varPath, ReserveType.Attr);
for (const key in bindings) {
bindings[key].extra!.reserve!.exportName =
currentProgramPath.scope.generateUid("apply_" + key);
const binding = bindings[key].extra!.reserve!;
binding!.exportIdentifier = getReferenceGroup(
sectionId,
binding,
true
).apply;
}
(currentProgramPath.node.extra ??= {}).attrs = {
bindings,

View File

@ -1,8 +1,17 @@
import { types as t } from "@marko/compiler";
import { Tag, assertNoParams, assertNoVar } from "@marko/babel-utils";
import { exitBranch } from "./if";
import { exitBranchTranslate, exitBranchAnalyze } from "./if";
import customTag from "../../visitors/tag/custom-tag";
export default {
analyze: {
enter(tag) {
customTag.analyze.enter(tag);
},
exit(tag) {
exitBranchAnalyze(tag);
},
},
translate: {
enter(tag) {
const { node } = tag;
@ -36,7 +45,7 @@ export default {
}
},
exit(tag) {
exitBranch(tag);
exitBranchTranslate(tag);
},
},
attributes: {},

View File

@ -1,8 +1,17 @@
import type { types as t } from "@marko/compiler";
import { Tag, assertNoParams, assertNoVar } from "@marko/babel-utils";
import { exitBranch } from "./if";
import { exitBranchTranslate, exitBranchAnalyze } from "./if";
import customTag from "../../visitors/tag/custom-tag";
export default {
analyze: {
enter(tag) {
customTag.analyze.enter(tag);
},
exit(tag) {
exitBranchAnalyze(tag);
},
},
translate: {
enter(tag) {
const { node } = tag;
@ -31,7 +40,7 @@ export default {
}
},
exit(tag) {
exitBranch(tag);
exitBranchTranslate(tag);
},
},
attributes: {},

View File

@ -2,7 +2,6 @@ import { types as t } from "@marko/compiler";
import { Tag, assertNoParams, assertNoVar } from "@marko/babel-utils";
import * as writer from "../../util/writer";
import * as walks from "../../util/walks";
import * as sorted from "../../util/sorted-arr";
import {
addStatement,
setQueueBuilder,
@ -12,16 +11,12 @@ import { callRuntime } from "../../util/runtime";
import { isCoreTagName } from "../../util/is-core-tag";
import toFirstStatementOrBlock from "../../util/to-first-statement-or-block";
import { getOrCreateSectionId, getSectionId } from "../../util/sections";
import {
Reserve,
ReserveType,
reserveScope,
compareReserves,
} from "../../util/reserve";
import { ReserveType, reserveScope } from "../../util/reserve";
import { isOutputDOM, isOutputHTML } from "../../util/marko-config";
import analyzeAttributeTags from "../../util/nested-attribute-tags";
import customTag from "../../visitors/tag/custom-tag";
import { scopeIdentifier } from "../../visitors/program";
import { mergeReferenceGroups, ReferenceGroup } from "../../util/references";
export default {
analyze: {
@ -37,6 +32,7 @@ export default {
},
exit(tag) {
analyzeAttributeTags(tag);
exitBranchAnalyze(tag);
},
},
translate: {
@ -78,7 +74,7 @@ export default {
}
},
exit(tag) {
exitBranch(tag);
exitBranchTranslate(tag);
},
},
attributes: {},
@ -99,29 +95,54 @@ const BRANCHES_LOOKUP = new WeakMap<
}[]
>();
export function exitBranch(tag: t.NodePath<t.MarkoTag>) {
const tagBody = tag.get("body");
const bodySectionId = getSectionId(tagBody);
function getBranches(tag: t.NodePath<t.MarkoTag>, bodySectionId: number) {
const branches = BRANCHES_LOOKUP.get(tag) ?? [];
const nextTag = tag.getNextSibling();
const isLast = !(
isCoreTagName(nextTag, "else") || isCoreTagName(nextTag, "else-if")
);
const branches = BRANCHES_LOOKUP.get(tag) || [];
const reserve = tag.node.extra.reserve!;
branches.push({
tag,
sectionId: bodySectionId,
});
setQueueBuilder(tag, ({ identifier, queuePriority }, closurePriority) =>
if (!isLast) {
BRANCHES_LOOKUP.set(nextTag as t.NodePath<t.MarkoTag>, branches);
}
return [isLast, branches] as const;
}
export function exitBranchAnalyze(tag: t.NodePath<t.MarkoTag>) {
const sectionId = getOrCreateSectionId(tag);
const tagBody = tag.get("body");
const bodySectionId = getOrCreateSectionId(tagBody);
const [isLast, branches] = getBranches(tag, bodySectionId);
if (isLast) {
branches[0].tag.node.extra.conditionalReferences = mergeReferenceGroups(
sectionId,
branches
.filter(({ tag }) => tag.node.attributes[0]?.extra?.valueReferences)
.map(({ tag }) => [tag.node.attributes[0].extra, "valueReferences"])
);
}
}
export function exitBranchTranslate(tag: t.NodePath<t.MarkoTag>) {
const tagBody = tag.get("body");
const bodySectionId = getSectionId(tagBody);
const reserve = tag.node.extra.reserve!;
const [isLast, branches] = getBranches(tag, bodySectionId);
setQueueBuilder(tag, ({ apply, index }, closurePriority) =>
callRuntime(
"queueInBranch",
scopeIdentifier,
t.numericLiteral(reserve.id),
writer.getRenderer(bodySectionId),
identifier,
queuePriority,
apply,
t.numericLiteral(index),
closurePriority
)
);
@ -135,7 +156,6 @@ export function exitBranch(tag: t.NodePath<t.MarkoTag>) {
if (isOutputDOM()) {
const sectionId = getSectionId(tag);
const { extra } = branches[0].tag.node;
const refs: Reserve[] = [];
let expr: t.Expression = t.nullLiteral();
for (let i = branches.length; i--; ) {
@ -146,17 +166,6 @@ export function exitBranch(tag: t.NodePath<t.MarkoTag>) {
tag.remove();
if (testAttr) {
const curRefs = testAttr.extra.valueReferences;
if (curRefs) {
if (Array.isArray(curRefs)) {
for (const ref of curRefs) {
sorted.insert(compareReserves, refs, ref);
}
} else {
sorted.insert(compareReserves, refs, curRefs);
}
}
expr = t.conditionalExpression(testAttr.value, id, expr);
} else {
expr = id;
@ -166,7 +175,9 @@ export function exitBranch(tag: t.NodePath<t.MarkoTag>) {
addStatement(
"apply",
sectionId,
refs.length === 0 ? undefined : refs.length === 1 ? refs[0] : refs,
// TODO: It's possible this group may not exist because the `refs`,
// created above, is a unique group that wasn't found during analyze
extra.conditionalReferences as ReferenceGroup,
t.expressionStatement(
callRuntime(
"setConditionalRenderer",
@ -177,6 +188,8 @@ export function exitBranch(tag: t.NodePath<t.MarkoTag>) {
)
);
} else {
const nextTag = tag.getNextSibling();
let statement: t.Statement | undefined;
for (let i = branches.length; i--; ) {
const { tag } = branches[i];
@ -194,7 +207,5 @@ export function exitBranch(tag: t.NodePath<t.MarkoTag>) {
nextTag.insertBefore(statement!);
}
} else {
BRANCHES_LOOKUP.set(nextTag as t.NodePath<t.MarkoTag>, branches);
}
}

View File

@ -4,7 +4,8 @@ import { assertNoBodyContent } from "../util/assert";
import translateVar from "../util/translate-var";
import { isOutputDOM } from "../util/marko-config";
import { getSectionId } from "../util/sections";
import { addStatement, bindingToApplyGroup } from "../util/apply-hydrate";
import { addStatement } from "../util/apply-hydrate";
import { getReferenceGroup } from "../util/references";
import { scopeIdentifier } from "../visitors/program";
export default {
@ -51,8 +52,8 @@ export default {
identifiers.length === 1
? t.expressionStatement(
t.callExpression(
bindingToApplyGroup(identifiers[0].extra.reserve!, sectionId)
.identifier,
getReferenceGroup(sectionId, identifiers[0].extra.reserve)
.apply,
[scopeIdentifier, defaultAttr.value]
)
)
@ -63,8 +64,8 @@ export default {
...identifiers.map((identifier) =>
t.expressionStatement(
t.callExpression(
bindingToApplyGroup(identifier.extra.reserve!, sectionId)
.identifier,
getReferenceGroup(sectionId, identifier.extra.reserve)
.apply,
[t.identifier(identifier.name)]
)
)

View File

@ -2,10 +2,7 @@ import { types as t } from "@marko/compiler";
import { Tag, assertNoParams } from "@marko/babel-utils";
import { assertNoBodyContent } from "../util/assert";
import { isOutputDOM } from "../util/marko-config";
import {
addStatement,
ensureHydrateReferenceGroup,
} from "../util/apply-hydrate";
import { addStatement, addHTMLHydrateCall } from "../util/apply-hydrate";
import { callRuntime } from "../util/runtime";
import { getSectionId } from "../util/sections";
import { ReserveType, reserveScope } from "../util/reserve";
@ -58,10 +55,7 @@ export default {
)
);
} else {
ensureHydrateReferenceGroup(
sectionId,
defaultAttr.extra?.valueReferences
);
addHTMLHydrateCall(sectionId, defaultAttr.extra?.valueReferences);
}
tag.remove();

View File

@ -10,7 +10,6 @@ import * as writer from "../util/writer";
import * as walks from "../util/walks";
import {
addStatement,
bindingToApplyGroup,
setQueueBuilder,
writeHTMLHydrateStatements,
} from "../util/apply-hydrate";
@ -20,6 +19,7 @@ import { callRuntime } from "../util/runtime";
import analyzeAttributeTags from "../util/nested-attribute-tags";
import customTag from "../visitors/tag/custom-tag";
import { scopeIdentifier } from "../visitors/program";
import { getReferenceGroup } from "../util/references";
export default {
analyze: {
@ -150,13 +150,13 @@ const translateDOM = {
const ofAttr = findName(attributes, "of");
const byAttr = findName(attributes, "by");
setQueueBuilder(tag, ({ identifier, queuePriority }, closurePriority) => {
setQueueBuilder(tag, ({ apply, index }, closurePriority) => {
return callRuntime(
"queueForEach",
scopeIdentifier,
t.numericLiteral(reserve!.id),
identifier,
queuePriority,
apply,
t.numericLiteral(index),
closurePriority
);
});
@ -189,8 +189,7 @@ const translateDOM = {
ofAttrValue,
rendererId,
byAttr ? byAttr.value! : t.nullLiteral(),
bindingToApplyGroup(valParam.extra.reserve!, bodySectionId)
.identifier
getReferenceGroup(bodySectionId, valParam.extra.reserve).apply
)
)
);

View File

@ -3,11 +3,12 @@ import { Tag, assertNoParams } from "@marko/babel-utils";
import { assertNoBodyContent } from "../util/assert";
import translateVar from "../util/translate-var";
import { isOutputDOM } from "../util/marko-config";
import { addStatement, bindingToApplyGroup } from "../util/apply-hydrate";
import { addStatement } from "../util/apply-hydrate";
import { callQueue } from "../util/runtime";
import replaceAssignments from "../util/replace-assignments";
import { getSectionId } from "../util/sections";
import { scopeIdentifier } from "../visitors/program";
import { getReferenceGroup } from "../util/references";
export default {
translate(tag) {
@ -51,22 +52,24 @@ export default {
if (isOutputDOM()) {
const sectionId = getSectionId(tag);
const binding = tagVar.extra.reserve!;
const applyGroup = bindingToApplyGroup(binding, sectionId);
const applyId = applyGroup.identifier;
const referenceGroup = getReferenceGroup(sectionId, binding);
// TODO: add defined guard if bindings exist.
addStatement(
"apply",
sectionId,
defaultAttr.extra?.valueReferences,
t.expressionStatement(
t.callExpression(applyId, [scopeIdentifier, defaultAttr.value])
t.callExpression(referenceGroup.apply, [
scopeIdentifier,
defaultAttr.value,
])
)
);
replaceAssignments(
tag.scope.getBinding(binding.name)!,
(assignment, value) =>
callQueue(applyGroup, binding, value, getSectionId(assignment))
callQueue(referenceGroup, binding, value, getSectionId(assignment))
);
} else {
translateVar(tag, defaultAttr.value);

View File

@ -1,31 +1,27 @@
import { types as t } from "@marko/compiler";
import type { References } from "../util/references";
import { getReferenceGroup, ReferenceGroup } from "../util/references";
import {
getSectionId,
createSectionState,
forEachSectionIdReverse,
getOrCreateSectionId,
} from "../util/sections";
import { Reserve, compareReserves } from "../util/reserve";
import * as sorted from "../util/sorted-arr";
import { Reserve, insertReserve } from "../util/reserve";
import { currentProgramPath, scopeIdentifier } from "../visitors/program";
import { callRuntime, callRead } from "./runtime";
import { getTemplateId } from "@marko/babel-utils";
export interface ReferenceGroup {
identifier: t.Identifier;
references: References;
statements: t.Statement[];
queuePriority: t.NumericLiteral;
}
export type queueBuilder = (
group: ReferenceGroup,
closurePriority: t.NumericLiteral
) => t.Expression;
const [getApply] = createSectionState<ReferenceGroup[]>("apply", () => []);
const [getHydrate] = createSectionState<ReferenceGroup[]>("hydrate", () => []);
const [getApplyStatements] = createSectionState<
Array<t.Statement[] | undefined>
>("applyStatements", () => []);
const [getHydrateStatements] = createSectionState<
Array<t.Statement[] | undefined>
>("hydrateStatements", () => []);
const [getQueueBuilder, _setQueueBuilder] =
createSectionState<queueBuilder>("queue");
@ -39,46 +35,27 @@ export function setQueueBuilder(
export function addStatement(
type: "apply" | "hydrate",
targetSectionId: number,
references: References,
references: ReferenceGroup | undefined,
statement: t.Statement | t.Statement[]
) {
const groups =
type === "apply" ? getApply(targetSectionId) : getHydrate(targetSectionId);
const existingGroup = getGroupByReferences(groups, references);
const isNew = !existingGroup;
const { statements } = isNew
? createAndInsertGroup(type, groups, targetSectionId, references)
: existingGroup;
const statementsIndex = references?.index ?? 0;
const allStatements =
type === "apply"
? getApplyStatements(targetSectionId)
: getHydrateStatements(targetSectionId);
const statements = (allStatements[statementsIndex] ??= []);
if (Array.isArray(statement)) {
statements.push(...statement);
} else {
statements.push(statement);
}
return isNew ? 1 : 0;
}
export function ensureHydrateReferenceGroup(
targetSectionId: number,
references: References
function getHydrateRegisterId(
sectionId: number,
references: ReferenceGroup["references"]
) {
const groups = getHydrate(targetSectionId);
const existingGroup = getGroupByReferences(groups, references);
if (!existingGroup) {
const identifier = t.identifier(
generateReferenceGroupName("hydrate", targetSectionId, references)
);
const group: ReferenceGroup = {
identifier,
references,
statements: [],
queuePriority: t.numericLiteral(-1),
};
sorted.insert(compareReferenceGroups, groups, group);
}
}
function getHydrateRegisterId(sectionId: number, references: References) {
const {
markoOpts: { optimize },
opts: { filename },
@ -96,43 +73,6 @@ function getHydrateRegisterId(sectionId: number, references: References) {
return `${getTemplateId(optimize, filename as string)}_${sectionId}${name}`;
}
export function bindingToApplyGroup(binding: Reserve, sectionId: number) {
const applyGroups = getApply(sectionId);
const group =
getGroupByReferences(applyGroups, binding) ??
createAndInsertGroup("apply", applyGroups, sectionId, binding);
return group;
}
function createAndInsertGroup(
type: "apply" | "hydrate",
groups: ReferenceGroup[],
sectionId: number,
references: References
) {
const identifier = t.identifier(
generateReferenceGroupName(type, sectionId, references)
);
const group: ReferenceGroup = {
identifier,
references,
statements: [],
queuePriority: t.numericLiteral(NaN),
};
sorted.insert(compareReferenceGroups, groups, group);
return group;
}
function getGroupByReferences(
groups: ReferenceGroup[],
references: References
) {
const groupIndex = sorted.findIndex(compareReferenceGroups, groups, {
references,
} as ReferenceGroup);
return groups[groupIndex];
}
export function writeAllStatementGroups() {
forEachSectionIdReverse((sectionId) => {
writeHydrateGroups(sectionId);
@ -141,14 +81,19 @@ export function writeAllStatementGroups() {
}
export function writeApplyGroups(sectionId: number) {
const groups = getApply(sectionId);
if (!groups.length) return;
const allStatements = getApplyStatements(sectionId);
if (!allStatements.length) return;
const closurePriorities = [];
for (let i = groups.length; i--; ) {
const group = groups[i];
const { identifier, references, statements, queuePriority } = group;
for (let i = allStatements.length; i--; ) {
const statements = allStatements[i] ?? [];
if (i === 0 && !statements.length) continue;
const referenceGroup = getReferenceGroup(sectionId, i);
const { references, apply: identifier } = referenceGroup;
const queuePriority = t.numericLiteral(i - 1);
let params: (t.Identifier | t.RestElement | t.Pattern)[];
let body: t.BlockStatement;
@ -163,10 +108,10 @@ export function writeApplyGroups(sectionId: number) {
body = t.blockStatement(statements);
for (const binding of references) {
i += addStatement(
addStatement(
"apply",
sectionId,
binding,
getReferenceGroup(sectionId, binding),
t.expressionStatement(
// TODO: might need to queue in a child scope
callRuntime("queue", scopeIdentifier, identifier, queuePriority)
@ -186,13 +131,13 @@ export function writeApplyGroups(sectionId: number) {
if (factory) {
const closurePriority = t.numericLiteral(NaN);
closurePriorities.push(closurePriority);
i += addStatement(
addStatement(
"apply",
references.sectionId,
references,
t.expressionStatement(factory(group, closurePriority))
getReferenceGroup(references.sectionId, references),
t.expressionStatement(factory(referenceGroup, closurePriority))
);
i += addStatement(
addStatement(
"apply",
sectionId,
undefined,
@ -230,19 +175,19 @@ export function writeApplyGroups(sectionId: number) {
fnPath.traverse(bindFunctionsVisitor, { root: fnPath, sectionId });
}
const offset = groups[0].references ? 0 : 1;
for (let i = offset; i < groups.length; i++) {
groups[i].queuePriority.value = i - offset;
}
for (let i = 0; i < closurePriorities.length; i++) {
closurePriorities[i].value = i + groups.length - offset;
closurePriorities[i].value = i + allStatements.length;
}
}
export function writeHydrateGroups(sectionId: number) {
const groups = getHydrate(sectionId);
for (let i = groups.length; i--; ) {
const { identifier, references, statements } = groups[i];
const allStatements = getHydrateStatements(sectionId);
for (let i = allStatements.length; i--; ) {
const statements = allStatements[i];
if (!statements?.length) continue;
const referenceGroup = getReferenceGroup(sectionId, i)!;
const { references, hydrate: identifier } = referenceGroup;
const params: Parameters<typeof t.functionDeclaration>[1] = references
? (Array.isArray(references) ? references : [references]).map((binding) =>
t.assignmentPattern(
@ -271,7 +216,7 @@ export function writeHydrateGroups(sectionId: number) {
addStatement(
"apply",
sectionId,
references,
getReferenceGroup(sectionId, references),
t.expressionStatement(
callRuntime("queueHydrate", scopeIdentifier, identifier)
)
@ -279,11 +224,18 @@ export function writeHydrateGroups(sectionId: number) {
}
}
export function addHTMLHydrateCall(
sectionId: number,
references?: ReferenceGroup
) {
addStatement("hydrate", sectionId, references, undefined as any);
}
export function writeHTMLHydrateStatements(
path: t.NodePath<t.MarkoTagBody | t.Program>
) {
const sectionId = getOrCreateSectionId(path);
const groups = getHydrate(sectionId);
const allStatements = getHydrateStatements(sectionId);
path.unshiftContainer(
"body",
@ -292,31 +244,33 @@ export function writeHTMLHydrateStatements(
])
);
if (!groups.length) return;
if (!allStatements.length) return;
const refs: Reserve[] = [];
for (let i = groups.length; i--; ) {
const { references } = groups[i];
if (references) {
if (Array.isArray(references)) {
for (const ref of references) {
sorted.insert(compareReserves, refs, ref);
for (let i = allStatements.length; i--; ) {
if (allStatements[i]?.length) {
const { references } = getReferenceGroup(sectionId, i)!;
if (references) {
if (Array.isArray(references)) {
for (const ref of references) {
insertReserve(refs, ref);
}
} else {
insertReserve(refs, references);
}
} else {
sorted.insert(compareReserves, refs, references);
}
}
path.pushContainer(
"body",
t.expressionStatement(
callRuntime(
"writeHydrateCall",
scopeIdentifier,
t.stringLiteral(getHydrateRegisterId(sectionId, references))
path.pushContainer(
"body",
t.expressionStatement(
callRuntime(
"writeHydrateCall",
scopeIdentifier,
t.stringLiteral(getHydrateRegisterId(sectionId, references))
)
)
)
);
);
}
}
path.pushContainer(
@ -339,69 +293,6 @@ export function writeHTMLHydrateStatements(
);
}
/**
* reference group priority is sorted by number of references,
* then if needed by reference order.
*/
function compareReferenceGroups(
{ references: a }: ReferenceGroup,
{ references: b }: ReferenceGroup
) {
if (a) {
if (b) {
if (Array.isArray(a)) {
if (Array.isArray(b)) {
const len = a.length;
const lenDelta = len - b.length;
if (lenDelta !== 0) {
return lenDelta;
}
for (let i = 0; i < len; i++) {
const compareResult = compareReserves(a[i], b[i]);
if (compareResult !== 0) {
return compareResult;
}
}
return 0;
} else {
return 1;
}
} else if (Array.isArray(b)) {
return -1;
} else {
return compareReserves(a, b);
}
} else {
return 1;
}
} else {
return b ? -1 : 0;
}
}
function generateReferenceGroupName(
type: "apply" | "hydrate",
sectionId: number,
references: References
) {
let name = type + (sectionId || "");
if (references) {
if (Array.isArray(references)) {
name += "With";
for (const ref of references) {
name += `_${ref.name}`;
}
} else {
name += `_${references.name}`;
}
}
return currentProgramPath.scope.generateUid(name);
}
const bindFunctionsVisitor: t.Visitor<{
root: t.NodePath<any>;
sectionId: number;
@ -416,7 +307,7 @@ function bindFunction(
) {
const { node } = fn;
const { extra } = node;
const references = extra?.references;
const references = extra?.references?.references;
const program = fn.hub.file.path;
const functionIdentifier = program.scope.generateUidIdentifier(extra?.name);
@ -449,8 +340,8 @@ function bindFunction(
}
export function getDefaultApply(sectionId: number) {
const [firstApply] = getApply(sectionId);
const defaultApply =
firstApply && !firstApply.references && firstApply.identifier;
return defaultApply || t.nullLiteral();
const [firstApplyStatements] = getApplyStatements(sectionId);
return firstApplyStatements
? getReferenceGroup(sectionId, 0).apply
: t.nullLiteral();
}

View File

@ -1,16 +1,14 @@
import { types as t } from "@marko/compiler";
import toPropertyName from "./to-property-name";
import * as sorted from "./sorted-arr";
import { compareReserves } from "./reserve";
import type { References } from "./references";
import type { ReferenceGroup } from "./references";
export default function attrsToObject(
tag: t.NodePath<t.MarkoTag>,
withRenderBody = false
): (t.Expression & { extra: { references: References } }) | undefined {
): t.Expression | undefined {
const { node } = tag;
let result: t.Expression = t.objectExpression([]);
const resultExtra: { references?: References } = (result.extra = {});
const resultExtra: { references?: ReferenceGroup } = (result.extra = {});
for (const attr of node.attributes) {
const value = attr.value!;
@ -22,28 +20,6 @@ export default function attrsToObject(
t.objectProperty(toPropertyName(attr.name), value)
);
}
const references = attr.extra?.valueReferences;
if (references) {
if (Array.isArray(references)) {
for (const binding of references) {
sorted.insertProp(
compareReserves,
resultExtra,
`references`,
binding
);
}
} else {
sorted.insertProp(
compareReserves,
resultExtra,
`references`,
references
);
}
}
}
if (withRenderBody) {
@ -85,7 +61,7 @@ export default function attrsToObject(
}
}
return result as t.Expression & { extra: { references: References } };
return result as t.Expression;
}
}

View File

@ -1,7 +1,18 @@
import type { types as t } from "@marko/compiler";
import * as sorted from "./sorted-arr";
import { getOrCreateSectionId } from "./sections";
import { Reserve, ReserveType, reserveScope, compareReserves } from "./reserve";
import { types as t } from "@marko/compiler";
import { createSortedCollection } from "./sorted-arr";
import {
getOrCreateSectionId,
createSectionState,
forEachSectionId,
} from "./sections";
import {
Reserve,
ReserveType,
reserveScope,
compareReserves,
insertReserve,
} from "./reserve";
import { currentProgramPath } from "../visitors/program";
type MarkoExprRootPath = t.NodePath<
| t.MarkoTag
@ -11,38 +22,62 @@ type MarkoExprRootPath = t.NodePath<
| t.MarkoPlaceholder
>;
export type References = undefined | Reserve | Reserve[];
const [getReferenceGroups] = createSectionState<ReferenceGroup[]>(
"apply",
() => [
{
sectionId: 0,
index: 0,
count: 0,
references: undefined,
apply: t.identifier(""),
hydrate: t.identifier(""),
},
]
);
export interface ReferenceGroup {
sectionId: number;
index: number;
count: number;
references: undefined | Reserve | Reserve[];
apply: t.Identifier;
hydrate: t.Identifier;
}
declare module "@marko/compiler/dist/types" {
export interface ProgramExtra {
referenceGroups?: ReferenceGroup[][];
}
export interface FunctionExpressionExtra {
references?: References;
references?: ReferenceGroup;
name?: string;
}
export interface ArrowFunctionExpressionExtra {
references?: References;
references?: ReferenceGroup;
name?: string;
}
export interface MarkoTagExtra {
varReferences?: References;
nameReferences?: References;
varReferences?: ReferenceGroup;
nameReferences?: ReferenceGroup;
}
export interface MarkoTagBodyExtra {
paramsReferences?: References;
paramsReferences?: ReferenceGroup;
}
export interface MarkoAttributeExtra {
valueReferences?: References;
valueReferences?: ReferenceGroup;
}
export interface MarkoSpreadAttributeExtra {
valueReferences?: References;
valueReferences?: ReferenceGroup;
}
export interface MarkoPlaceholderExtra {
valueReferences?: References;
valueReferences?: ReferenceGroup;
}
}
@ -72,30 +107,34 @@ export function trackReferencesForBindings(
const identifier = bindings[name];
const binding = reserveScope(reserveType, sectionId, identifier, name);
insertReferenceGroup(getReferenceGroups(sectionId), {
sectionId,
index: 0,
count: 0,
references: binding,
apply: t.identifier(""),
hydrate: t.identifier(""),
});
for (const reference of references) {
const fnRoot = getFnRoot(reference.scope.path);
const exprRoot = getExprRoot(fnRoot || reference);
const exprExtra = (exprRoot.parentPath.node.extra ??= {});
const markoRoot = exprRoot.parentPath;
if (fnRoot) {
const fnExtra = (fnRoot.node.extra ??= {});
let name = (fnRoot.node as t.FunctionExpression).id?.name;
const name = (fnRoot.node as t.FunctionExpression).id?.name;
if (!name) {
const { parentPath } = exprRoot;
if (parentPath.isMarkoAttribute() && !parentPath.node.default) {
name = parentPath.node.name;
if (markoRoot.isMarkoAttribute() && !markoRoot.node.default) {
(fnRoot.node.extra ??= {}).name = markoRoot.node.name;
}
}
fnExtra.name = name;
sorted.insertProp(compareReserves, fnExtra, "references", binding);
updateReferenceGroup(fnRoot, "references", binding);
}
sorted.insertProp(
compareReserves,
exprExtra,
updateReferenceGroup(
markoRoot,
`${exprRoot.listKey || exprRoot.key}References`,
binding
);
@ -103,6 +142,82 @@ export function trackReferencesForBindings(
}
}
function updateReferenceGroup(
path: t.NodePath<t.Marko | t.ArrowFunctionExpression | t.FunctionExpression>,
extraKey: string,
newBinding: Reserve
) {
const sectionId = getOrCreateSectionId(path);
const currentGroup = (path.node.extra ??= {})[extraKey] as
| ReferenceGroup
| undefined;
const newReferences = insertReserve(
currentGroup?.references,
newBinding,
true
);
if (currentGroup) {
currentGroup.count--;
}
path.node.extra![extraKey] = getOrCreateReferenceGroup(
sectionId,
newReferences
);
}
export function mergeReferenceGroups(
sectionId: number,
groupEntries: [Record<string, unknown>, string][]
) {
let newReferences: ReferenceGroup["references"];
for (const [extra, key] of groupEntries) {
const group = extra[key] as ReferenceGroup;
const references = group.references;
delete extra[key];
group.count--;
sectionId = group.sectionId;
if (references) {
if (Array.isArray(references)) {
for (const binding of references) {
newReferences = insertReserve(newReferences, binding);
}
} else {
newReferences = insertReserve(newReferences, references);
}
}
}
return getOrCreateReferenceGroup(sectionId, newReferences);
}
function getOrCreateReferenceGroup(
sectionId: number,
references: ReferenceGroup["references"]
) {
const newGroup: ReferenceGroup = {
sectionId,
index: 0,
count: 1,
references,
apply: t.identifier(""),
hydrate: t.identifier(""),
};
const referenceGroups = getReferenceGroups(sectionId);
const existingGroup = findReferenceGroup(referenceGroups, newGroup);
if (existingGroup) {
existingGroup.count++;
} else {
insertReferenceGroup(referenceGroups, newGroup);
}
return existingGroup ?? newGroup;
}
function getExprRoot(path: t.NodePath<t.Node>) {
let curPath = path;
while (!isMarkoPath(curPath.parentPath!)) {
@ -152,3 +267,117 @@ function isFunctionExpression(
return false;
}
}
/**
* reference group priority is sorted by number of references,
* then if needed by reference order.
*/
const { insert: insertReferenceGroup, find: findReferenceGroup } =
createSortedCollection(function compareReferenceGroups(
{ references: a }: ReferenceGroup,
{ references: b }: ReferenceGroup
) {
if (a) {
if (b) {
if (Array.isArray(a)) {
if (Array.isArray(b)) {
const len = a.length;
const lenDelta = len - b.length;
if (lenDelta !== 0) {
return lenDelta;
}
for (let i = 0; i < len; i++) {
const compareResult = compareReserves(a[i], b[i]);
if (compareResult !== 0) {
return compareResult;
}
}
return 0;
} else {
return 1;
}
} else if (Array.isArray(b)) {
return -1;
} else {
return compareReserves(a, b);
}
} else {
return 1;
}
} else {
return b ? -1 : 0;
}
});
export function finalizeReferences() {
const allReferenceGroups: ReferenceGroup[][] = [];
forEachSectionId((sectionId) => {
const referenceGroups = getReferenceGroups(sectionId).filter(
(g) => g.count > 0 || !Array.isArray(g.references)
);
referenceGroups.forEach((g, i) => {
g.index = i;
g.apply.name = generateReferenceGroupName(
"apply",
sectionId,
g.references
);
g.hydrate.name = generateReferenceGroupName(
"hydrate",
sectionId,
g.references
);
});
allReferenceGroups[sectionId] = referenceGroups;
});
(currentProgramPath.node.extra ??= {}).referenceGroups = allReferenceGroups;
}
export function getReferenceGroup(
sectionId: number,
lookup: number | ReferenceGroup["references"],
analyze = false
) {
const referenceGroups = analyze
? getReferenceGroups(sectionId)
: currentProgramPath.node.extra!.referenceGroups![sectionId];
let found: ReferenceGroup | undefined;
if (typeof lookup === "number") {
found = referenceGroups[lookup];
} else {
found = findReferenceGroup(referenceGroups, {
references: lookup,
} as ReferenceGroup);
}
if (!found) {
throw new Error(
`Reference group not found for section ${sectionId}: ${lookup}`
);
}
return found;
}
function generateReferenceGroupName(
type: "apply" | "hydrate",
sectionId: number,
references: ReferenceGroup["references"]
) {
let name = type + (sectionId || "");
if (references) {
if (Array.isArray(references)) {
name += "With";
for (const ref of references) {
name += `_${ref.name}`;
}
} else {
name += `_${references.name}`;
}
}
return currentProgramPath.scope.generateUid(name);
}

View File

@ -1,5 +1,6 @@
import type { types as t } from "@marko/compiler";
import { createSectionState, forEachSectionId } from "./sections";
import { createSortedCollection } from "./sorted-arr";
const [getReservesByType] = createSectionState<
[Reserve[] | undefined, Reserve[] | undefined, Reserve[] | undefined]
@ -17,7 +18,7 @@ export interface Reserve {
name: string;
size: number;
id: number;
exportName?: string;
exportIdentifier?: t.Identifier;
}
declare module "@marko/compiler/dist/types" {
@ -99,3 +100,6 @@ export function assignFinalIds() {
export function compareReserves(a: Reserve, b: Reserve) {
return a.sectionId - b.sectionId || a.type - b.type || a.id - b.id;
}
export const { insert: insertReserve } =
createSortedCollection(compareReserves);

View File

@ -3,7 +3,7 @@ import { importNamed } from "@marko/babel-utils";
import { getMarkoOpts } from "./marko-config";
import type { Reserve } from "./reserve";
import { currentProgramPath, scopeIdentifier } from "../visitors/program";
import type { ReferenceGroup } from "./apply-hydrate";
import type { ReferenceGroup } from "./references";
declare const MARKO_SRC: boolean;
@ -49,7 +49,7 @@ export function callRead(reference: Reserve, targetSectionId: number) {
}
export function callQueue(
{ identifier, queuePriority }: ReferenceGroup,
{ apply, index }: ReferenceGroup,
reference: Reserve,
value: t.Expression,
targetSectionId: number
@ -57,8 +57,8 @@ export function callQueue(
return callRuntime(
"queue",
getScopeExpression(reference, targetSectionId),
identifier,
queuePriority,
apply,
t.numericLiteral(index - 1),
value
);
}

View File

@ -1,8 +1,8 @@
export function insert<T extends unknown[]>(
function insertInArray<T extends unknown[]>(
compare: (a: T[number], b: T[number]) => number,
arr: T,
val: T[number]
) {
): T {
const len = arr.length;
let max = len;
let pos = 0;
@ -10,7 +10,7 @@ export function insert<T extends unknown[]>(
while (pos < max) {
const mid = (pos + max) >>> 1;
const compareResult = compare(arr[mid], val);
if (compareResult === 0) return;
if (compareResult === 0) return arr;
if (compareResult > 0) max = mid;
else pos = mid + 1;
}
@ -23,12 +23,14 @@ export function insert<T extends unknown[]>(
}
arr[len] = cur;
return arr;
}
export function findIndex<T extends unknown[]>(
compare: (a: T[number], b: T[number]) => number,
arr: T,
val: T[number]
export function findIndex<V>(
compare: (a: V, b: V) => number,
arr: V[],
val: V
) {
let max = arr.length;
let pos = 0;
@ -44,33 +46,30 @@ export function findIndex<T extends unknown[]>(
return -1;
}
type KeysWithValueType<T, M> = keyof {
[K in keyof T as string extends K
? never
: number extends K
? never
: T[K] extends undefined | M | M[]
? K
: never]: never;
};
export function createSortedCollection<V>(compare: (a: V, b: V) => number) {
return {
insert(data: undefined | V | V[], val: V, immutable = false): V | V[] {
if (data) {
if (Array.isArray(data)) {
return insertInArray(compare, immutable ? [...data] : data, val);
} else {
const compareResult = compare(data, val);
export function insertProp<
V,
T extends Record<string, unknown>,
K extends string extends K ? keyof T : KeysWithValueType<T, V>
>(compare: (a: V, b: V) => number, data: T, key: K & keyof T, val: V) {
const cur = data[key] as undefined | V | V[];
if (cur) {
if (Array.isArray(cur)) {
insert(compare, cur, val);
} else {
const compareResult = compare(cur, val);
if (compareResult !== 0) {
(data[key] as V[]) = compareResult < 0 ? [cur, val] : [val, cur];
if (compareResult !== 0) {
return compareResult < 0 ? [data, val] : [val, data];
}
}
}
}
} else {
(data[key] as V) = val;
}
return val;
},
find(data: undefined | V | V[], val: V) {
if (data) {
if (Array.isArray(data)) {
return data[findIndex(compare, data, val)];
} else {
return data === val ? data : undefined;
}
}
},
};
}

View File

@ -1,13 +1,11 @@
import { types as t } from "@marko/compiler";
import { callRuntime } from "../../util/runtime";
import { forEachSectionId, getSectionId } from "../../util/sections";
import {
bindingToApplyGroup,
writeAllStatementGroups,
} from "../../util/apply-hydrate";
import { writeAllStatementGroups } from "../../util/apply-hydrate";
import * as writer from "../../util/writer";
import { visit } from "../../util/walks";
import { scopeIdentifier } from ".";
import { getReferenceGroup } from "../../util/references";
export default {
translate: {
@ -57,17 +55,14 @@ export default {
t.blockStatement(
Object.keys(attrs.bindings).map((name) => {
const bindingIdentifier = attrs.bindings[name];
const exportName =
bindingIdentifier.extra!.reserve!.exportName!;
const { identifier: applyIdentifier } =
bindingToApplyGroup(
bindingIdentifier.extra!.reserve!,
sectionId
);
const { apply: applyIdentifier } = getReferenceGroup(
sectionId,
bindingIdentifier.extra!.reserve
);
exportSpecifiers.push(
t.exportSpecifier(
applyIdentifier,
t.identifier(exportName)
bindingIdentifier.extra!.reserve!.exportIdentifier!
)
);
return t.expressionStatement(

View File

@ -4,6 +4,7 @@ import programHTML from "./html";
import programDOM from "./dom";
import { startSection } from "../../util/sections";
import { assignFinalIds } from "../../util/reserve";
import { finalizeReferences } from "../../util/references";
export let currentProgramPath: t.NodePath<t.Program>;
export let scopeIdentifier: t.Identifier;
@ -24,6 +25,7 @@ export default {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
exit() {
assignFinalIds();
finalizeReferences();
currentProgramPath = previousProgramPath.get(currentProgramPath)!;
},
},

View File

@ -17,7 +17,10 @@ import {
getSectionId,
getOrCreateSectionId,
} from "../../util/sections";
import trackReferences from "../../util/references";
import trackReferences, {
mergeReferenceGroups,
ReferenceGroup,
} from "../../util/references";
import {
addStatement,
writeHTMLHydrateStatements,
@ -43,6 +46,20 @@ export default {
);
}
},
exit(tag: t.NodePath<t.MarkoTag>) {
// TODO: only if dynamic attributes
const tagDef = getTagDef(tag);
const template = tagDef?.template;
const sectionId = getOrCreateSectionId(tag);
if (template) {
tag.node.extra.attrsReferences = mergeReferenceGroups(
sectionId,
tag.node.attributes
.filter((attr) => attr.extra?.valueReferences)
.map((attr) => [attr.extra, "valueReferences"])
);
}
},
},
translate: {
enter(tag: t.NodePath<t.MarkoTag>) {
@ -204,7 +221,7 @@ export default {
addStatement(
"apply",
tagSectionId,
attrsObject.extra.references,
tag.node.extra.attrsReferences as ReferenceGroup,
t.expressionStatement(
t.callExpression(tagAttrsIdentifier, [
callRead(binding, tagSectionId),

View File

@ -58,7 +58,7 @@ export default {
analyzeAttributeTags(tag);
switch (type) {
case TagNameTypes.CustomTag:
// CustomTag.analyze.exit(tag);
CustomTag.analyze.exit(tag);
break;
case TagNameTypes.AttributeTag:
// AttributeTag.analyze.exit(tag);

View File

@ -7,10 +7,7 @@ import translateVar from "../../util/translate-var";
import evaluate from "../../util/evaluate";
import { getOrCreateSectionId, getSectionId } from "../../util/sections";
import { ReserveType, reserveScope } from "../../util/reserve";
import {
addStatement,
ensureHydrateReferenceGroup,
} from "../../util/apply-hydrate";
import { addStatement, addHTMLHydrateCall } from "../../util/apply-hydrate";
import * as writer from "../../util/writer";
import * as walks from "../../util/walks";
import { scopeIdentifier } from "../program";
@ -124,7 +121,7 @@ export default {
write`${getHTMLRuntime().attr(name, computed)}`;
} else if (isHTML) {
if (name.startsWith("on")) {
ensureHydrateReferenceGroup(sectionId, extra.valueReferences);
addHTMLHydrateCall(sectionId, extra.valueReferences);
} else {
write`${callRuntime(
"attr",