@marko/runtime-tags 6.1.12 → 6.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/errors.d.ts +2 -0
- package/dist/debug/dom.js +19 -3
- package/dist/debug/dom.mjs +19 -3
- package/dist/debug/html.js +22 -4
- package/dist/debug/html.mjs +22 -4
- package/dist/dom.js +3 -2
- package/dist/dom.mjs +3 -2
- package/dist/translator/index.js +26 -5
- package/package.json +1 -1
package/dist/common/errors.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export declare function _el_read_error(): void;
|
|
|
2
2
|
export declare function _hoist_read_error(): void;
|
|
3
3
|
export declare function _assert_hoist(value: unknown): void;
|
|
4
4
|
export declare function assertExclusiveAttrs(attrs: Record<string, unknown> | undefined, onError?: typeof throwErr): void;
|
|
5
|
+
export declare function assertValidEventHandlerAttr(name: string, value: unknown): void;
|
|
6
|
+
export declare function assertHandlerIsFunction(name: string, value: unknown): void;
|
|
5
7
|
export declare function assertValidTagName(tagName: string): void;
|
|
6
8
|
declare function throwErr(msg: string): void;
|
|
7
9
|
export {};
|
package/dist/debug/dom.js
CHANGED
|
@@ -22,6 +22,7 @@ function* attrTagIterator() {
|
|
|
22
22
|
}
|
|
23
23
|
//#endregion
|
|
24
24
|
//#region src/common/errors.ts
|
|
25
|
+
const lowercaseEventHandlerReg = /^on[a-z]/;
|
|
25
26
|
function _el_read_error() {
|
|
26
27
|
throw new Error("Element references can only be read in scripts and event handlers.");
|
|
27
28
|
}
|
|
@@ -46,6 +47,12 @@ function assertExclusiveAttrs(attrs, onError = throwErr) {
|
|
|
46
47
|
if (exclusiveAttrs && exclusiveAttrs.length > 1) onError(`The attributes ${joinWithAnd(exclusiveAttrs)} are mutually exclusive.`);
|
|
47
48
|
}
|
|
48
49
|
}
|
|
50
|
+
function assertValidEventHandlerAttr(name, value) {
|
|
51
|
+
if (value && typeof value !== "string" && lowercaseEventHandlerReg.test(name)) throw new Error(`The \`${name}\` attribute must be a string or a falsey value (\`null\`, \`undefined\`, \`false\`, \`0\`, …), but received type "${typeof value}". To attach an event listener, use the \`on${name[2].toUpperCase()}${name.slice(3)}\` event handler instead.`);
|
|
52
|
+
}
|
|
53
|
+
function assertHandlerIsFunction(name, value) {
|
|
54
|
+
if (value && typeof value !== "function") throw new Error(`The \`${name}\` handler must be a function or a falsey value (\`null\`, \`undefined\`, \`false\`, \`0\`, …), but received type "${typeof value}".`);
|
|
55
|
+
}
|
|
49
56
|
function assertValidTagName(tagName) {
|
|
50
57
|
if (!/^[a-z][a-z0-9._-]*$/i.test(tagName)) throw new Error(`Invalid tag name: "${tagName}". Tag names must start with a letter and contain only letters, numbers, periods, hyphens, and underscores.`);
|
|
51
58
|
}
|
|
@@ -146,6 +153,7 @@ function push(opt, item) {
|
|
|
146
153
|
//#region src/dom/event.ts
|
|
147
154
|
const defaultDelegator = /* @__PURE__ */ createDelegator();
|
|
148
155
|
function _on(element, type, handler) {
|
|
156
|
+
assertHandlerIsFunction("on" + type[0].toUpperCase() + type.slice(1), handler);
|
|
149
157
|
if (element["$" + type] === void 0) defaultDelegator(element, type, handleDelegated);
|
|
150
158
|
element["$" + type] = handler || null;
|
|
151
159
|
}
|
|
@@ -793,6 +801,7 @@ function _attr_input_checked_default(scope, nodeAccessor, checked) {
|
|
|
793
801
|
function _attr_input_checked(scope, nodeAccessor, checked, checkedChange) {
|
|
794
802
|
const el = scope[nodeAccessor];
|
|
795
803
|
const normalizedChecked = isNotVoid(checked);
|
|
804
|
+
assertHandlerIsFunction("checkedChange", checkedChange);
|
|
796
805
|
scope["ControlledHandler:" + nodeAccessor] = checkedChange;
|
|
797
806
|
scope["ControlledType:" + nodeAccessor] = checkedChange ? 0 : 5;
|
|
798
807
|
if (checkedChange && scope["#Gen"] < runId) el.checked = normalizedChecked;
|
|
@@ -821,6 +830,7 @@ function _attr_input_checkedValue(scope, nodeAccessor, checkedValue, checkedValu
|
|
|
821
830
|
const el = scope[nodeAccessor];
|
|
822
831
|
const multiple = Array.isArray(checkedValue);
|
|
823
832
|
const normalizedCheckedValue = scope["ControlledValue:" + nodeAccessor] = multiple ? checkedValue.map(normalizeStrProp) : normalizeStrProp(checkedValue);
|
|
833
|
+
assertHandlerIsFunction("checkedValueChange", checkedValueChange);
|
|
824
834
|
scope["ControlledHandler:" + nodeAccessor] = checkedValueChange;
|
|
825
835
|
scope["ControlledType:" + nodeAccessor] = checkedValueChange ? 1 : 5;
|
|
826
836
|
if (checkedValueChange && scope["#Gen"] < runId) {
|
|
@@ -857,6 +867,7 @@ function _attr_input_value_default(scope, nodeAccessor, value) {
|
|
|
857
867
|
function _attr_input_value(scope, nodeAccessor, value, valueChange) {
|
|
858
868
|
const el = scope[nodeAccessor];
|
|
859
869
|
const normalizedValue = normalizeAttrValue(value) || "";
|
|
870
|
+
assertHandlerIsFunction("valueChange", valueChange);
|
|
860
871
|
scope["ControlledHandler:" + nodeAccessor] = valueChange;
|
|
861
872
|
scope["ControlledValue:" + nodeAccessor] = normalizedValue;
|
|
862
873
|
scope["ControlledType:" + nodeAccessor] = valueChange ? 2 : 5;
|
|
@@ -905,6 +916,7 @@ function _attr_select_value(scope, nodeAccessor, value, valueChange) {
|
|
|
905
916
|
const existing = scope["#Gen"] < runId;
|
|
906
917
|
const multiple = Array.isArray(value);
|
|
907
918
|
const normalizedValue = scope["ControlledValue:" + nodeAccessor] = multiple ? value.map(normalizeStrProp) : normalizeStrProp(value);
|
|
919
|
+
assertHandlerIsFunction("valueChange", valueChange);
|
|
908
920
|
scope["ControlledHandler:" + nodeAccessor] = valueChange;
|
|
909
921
|
scope["ControlledType:" + nodeAccessor] = valueChange ? 3 : 5;
|
|
910
922
|
if (valueChange && existing) pendingEffects.unshift(() => setSelectValue(el, normalizedValue, multiple), scope);
|
|
@@ -954,6 +966,7 @@ function _attr_details_or_dialog_open_default(scope, nodeAccessor, open) {
|
|
|
954
966
|
}
|
|
955
967
|
function _attr_details_or_dialog_open(scope, nodeAccessor, open, openChange) {
|
|
956
968
|
const normalizedOpen = scope["ControlledValue:" + nodeAccessor] = isNotVoid(open);
|
|
969
|
+
assertHandlerIsFunction("openChange", openChange);
|
|
957
970
|
scope["ControlledHandler:" + nodeAccessor] = openChange;
|
|
958
971
|
scope["ControlledType:" + nodeAccessor] = openChange ? 4 : 5;
|
|
959
972
|
if (openChange && scope["#Gen"] < runId) scope[nodeAccessor].open = normalizedOpen;
|
|
@@ -1015,6 +1028,7 @@ function _to_text(value) {
|
|
|
1015
1028
|
return value || value === 0 ? value + "" : "";
|
|
1016
1029
|
}
|
|
1017
1030
|
function _attr(element, name, value) {
|
|
1031
|
+
assertValidEventHandlerAttr(name, value);
|
|
1018
1032
|
setAttribute(element, name, normalizeAttrValue(value));
|
|
1019
1033
|
}
|
|
1020
1034
|
function setAttribute(element, name, value) {
|
|
@@ -1089,8 +1103,9 @@ function _attrs_partial_content(scope, nodeAccessor, nextAttrs, skip) {
|
|
|
1089
1103
|
}
|
|
1090
1104
|
function attrsInternal(scope, nodeAccessor, nextAttrs) {
|
|
1091
1105
|
const el = scope[nodeAccessor];
|
|
1092
|
-
let events;
|
|
1106
|
+
let events = scope["EventAttributes:" + nodeAccessor];
|
|
1093
1107
|
let skip;
|
|
1108
|
+
for (const name in events) events[name] = 0;
|
|
1094
1109
|
switch (el.tagName) {
|
|
1095
1110
|
case "INPUT":
|
|
1096
1111
|
if ("checked" in nextAttrs || "checkedChange" in nextAttrs) _attr_input_checked(scope, nodeAccessor, nextAttrs.checked, nextAttrs.checkedChange);
|
|
@@ -1374,7 +1389,7 @@ function _if(nodeAccessor, ...branchesArgs) {
|
|
|
1374
1389
|
while (i < branchesArgs.length) branches.push(_content("", branchesArgs[i++], branchesArgs[i++], branchesArgs[i++])());
|
|
1375
1390
|
enableBranches();
|
|
1376
1391
|
return (scope, newBranch) => {
|
|
1377
|
-
if (newBranch !== scope[branchAccessor]) setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
|
|
1392
|
+
if (newBranch !== (scope[branchAccessor] ?? (scope["BranchScopes:" + nodeAccessor] && 0))) setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
|
|
1378
1393
|
};
|
|
1379
1394
|
}
|
|
1380
1395
|
function patchDynamicTag(fn) {
|
|
@@ -1466,7 +1481,8 @@ function loop(forEach) {
|
|
|
1466
1481
|
let hasPotentialMoves;
|
|
1467
1482
|
var seenKeys = /* @__PURE__ */ new Set();
|
|
1468
1483
|
forEach(value, (key, args) => {
|
|
1469
|
-
if (
|
|
1484
|
+
if (typeof key !== "string" && typeof key !== "number") console.error(`A <for> tag's \`by\` attribute must return a string or number, but it returned:`, key);
|
|
1485
|
+
else if (seenKeys.has(key)) console.error(`A <for> tag's \`by\` attribute must return a unique value for each item, but a duplicate was found matching:`, key);
|
|
1470
1486
|
else seenKeys.add(key);
|
|
1471
1487
|
let branch = oldLen && (oldScopesByKey ||= oldScopes.reduce((map, scope, i) => map.set(scope["#LoopKey"] ?? i, scope), /* @__PURE__ */ new Map())).get(key);
|
|
1472
1488
|
if (branch) hasPotentialMoves = oldScopesByKey.delete(key);
|
package/dist/debug/dom.mjs
CHANGED
|
@@ -20,6 +20,7 @@ function* attrTagIterator() {
|
|
|
20
20
|
}
|
|
21
21
|
//#endregion
|
|
22
22
|
//#region src/common/errors.ts
|
|
23
|
+
const lowercaseEventHandlerReg = /^on[a-z]/;
|
|
23
24
|
function _el_read_error() {
|
|
24
25
|
throw new Error("Element references can only be read in scripts and event handlers.");
|
|
25
26
|
}
|
|
@@ -44,6 +45,12 @@ function assertExclusiveAttrs(attrs, onError = throwErr) {
|
|
|
44
45
|
if (exclusiveAttrs && exclusiveAttrs.length > 1) onError(`The attributes ${joinWithAnd(exclusiveAttrs)} are mutually exclusive.`);
|
|
45
46
|
}
|
|
46
47
|
}
|
|
48
|
+
function assertValidEventHandlerAttr(name, value) {
|
|
49
|
+
if (value && typeof value !== "string" && lowercaseEventHandlerReg.test(name)) throw new Error(`The \`${name}\` attribute must be a string or a falsey value (\`null\`, \`undefined\`, \`false\`, \`0\`, …), but received type "${typeof value}". To attach an event listener, use the \`on${name[2].toUpperCase()}${name.slice(3)}\` event handler instead.`);
|
|
50
|
+
}
|
|
51
|
+
function assertHandlerIsFunction(name, value) {
|
|
52
|
+
if (value && typeof value !== "function") throw new Error(`The \`${name}\` handler must be a function or a falsey value (\`null\`, \`undefined\`, \`false\`, \`0\`, …), but received type "${typeof value}".`);
|
|
53
|
+
}
|
|
47
54
|
function assertValidTagName(tagName) {
|
|
48
55
|
if (!/^[a-z][a-z0-9._-]*$/i.test(tagName)) throw new Error(`Invalid tag name: "${tagName}". Tag names must start with a letter and contain only letters, numbers, periods, hyphens, and underscores.`);
|
|
49
56
|
}
|
|
@@ -144,6 +151,7 @@ function push(opt, item) {
|
|
|
144
151
|
//#region src/dom/event.ts
|
|
145
152
|
const defaultDelegator = /* @__PURE__ */ createDelegator();
|
|
146
153
|
function _on(element, type, handler) {
|
|
154
|
+
assertHandlerIsFunction("on" + type[0].toUpperCase() + type.slice(1), handler);
|
|
147
155
|
if (element["$" + type] === void 0) defaultDelegator(element, type, handleDelegated);
|
|
148
156
|
element["$" + type] = handler || null;
|
|
149
157
|
}
|
|
@@ -791,6 +799,7 @@ function _attr_input_checked_default(scope, nodeAccessor, checked) {
|
|
|
791
799
|
function _attr_input_checked(scope, nodeAccessor, checked, checkedChange) {
|
|
792
800
|
const el = scope[nodeAccessor];
|
|
793
801
|
const normalizedChecked = isNotVoid(checked);
|
|
802
|
+
assertHandlerIsFunction("checkedChange", checkedChange);
|
|
794
803
|
scope["ControlledHandler:" + nodeAccessor] = checkedChange;
|
|
795
804
|
scope["ControlledType:" + nodeAccessor] = checkedChange ? 0 : 5;
|
|
796
805
|
if (checkedChange && scope["#Gen"] < runId) el.checked = normalizedChecked;
|
|
@@ -819,6 +828,7 @@ function _attr_input_checkedValue(scope, nodeAccessor, checkedValue, checkedValu
|
|
|
819
828
|
const el = scope[nodeAccessor];
|
|
820
829
|
const multiple = Array.isArray(checkedValue);
|
|
821
830
|
const normalizedCheckedValue = scope["ControlledValue:" + nodeAccessor] = multiple ? checkedValue.map(normalizeStrProp) : normalizeStrProp(checkedValue);
|
|
831
|
+
assertHandlerIsFunction("checkedValueChange", checkedValueChange);
|
|
822
832
|
scope["ControlledHandler:" + nodeAccessor] = checkedValueChange;
|
|
823
833
|
scope["ControlledType:" + nodeAccessor] = checkedValueChange ? 1 : 5;
|
|
824
834
|
if (checkedValueChange && scope["#Gen"] < runId) {
|
|
@@ -855,6 +865,7 @@ function _attr_input_value_default(scope, nodeAccessor, value) {
|
|
|
855
865
|
function _attr_input_value(scope, nodeAccessor, value, valueChange) {
|
|
856
866
|
const el = scope[nodeAccessor];
|
|
857
867
|
const normalizedValue = normalizeAttrValue(value) || "";
|
|
868
|
+
assertHandlerIsFunction("valueChange", valueChange);
|
|
858
869
|
scope["ControlledHandler:" + nodeAccessor] = valueChange;
|
|
859
870
|
scope["ControlledValue:" + nodeAccessor] = normalizedValue;
|
|
860
871
|
scope["ControlledType:" + nodeAccessor] = valueChange ? 2 : 5;
|
|
@@ -903,6 +914,7 @@ function _attr_select_value(scope, nodeAccessor, value, valueChange) {
|
|
|
903
914
|
const existing = scope["#Gen"] < runId;
|
|
904
915
|
const multiple = Array.isArray(value);
|
|
905
916
|
const normalizedValue = scope["ControlledValue:" + nodeAccessor] = multiple ? value.map(normalizeStrProp) : normalizeStrProp(value);
|
|
917
|
+
assertHandlerIsFunction("valueChange", valueChange);
|
|
906
918
|
scope["ControlledHandler:" + nodeAccessor] = valueChange;
|
|
907
919
|
scope["ControlledType:" + nodeAccessor] = valueChange ? 3 : 5;
|
|
908
920
|
if (valueChange && existing) pendingEffects.unshift(() => setSelectValue(el, normalizedValue, multiple), scope);
|
|
@@ -952,6 +964,7 @@ function _attr_details_or_dialog_open_default(scope, nodeAccessor, open) {
|
|
|
952
964
|
}
|
|
953
965
|
function _attr_details_or_dialog_open(scope, nodeAccessor, open, openChange) {
|
|
954
966
|
const normalizedOpen = scope["ControlledValue:" + nodeAccessor] = isNotVoid(open);
|
|
967
|
+
assertHandlerIsFunction("openChange", openChange);
|
|
955
968
|
scope["ControlledHandler:" + nodeAccessor] = openChange;
|
|
956
969
|
scope["ControlledType:" + nodeAccessor] = openChange ? 4 : 5;
|
|
957
970
|
if (openChange && scope["#Gen"] < runId) scope[nodeAccessor].open = normalizedOpen;
|
|
@@ -1013,6 +1026,7 @@ function _to_text(value) {
|
|
|
1013
1026
|
return value || value === 0 ? value + "" : "";
|
|
1014
1027
|
}
|
|
1015
1028
|
function _attr(element, name, value) {
|
|
1029
|
+
assertValidEventHandlerAttr(name, value);
|
|
1016
1030
|
setAttribute(element, name, normalizeAttrValue(value));
|
|
1017
1031
|
}
|
|
1018
1032
|
function setAttribute(element, name, value) {
|
|
@@ -1087,8 +1101,9 @@ function _attrs_partial_content(scope, nodeAccessor, nextAttrs, skip) {
|
|
|
1087
1101
|
}
|
|
1088
1102
|
function attrsInternal(scope, nodeAccessor, nextAttrs) {
|
|
1089
1103
|
const el = scope[nodeAccessor];
|
|
1090
|
-
let events;
|
|
1104
|
+
let events = scope["EventAttributes:" + nodeAccessor];
|
|
1091
1105
|
let skip;
|
|
1106
|
+
for (const name in events) events[name] = 0;
|
|
1092
1107
|
switch (el.tagName) {
|
|
1093
1108
|
case "INPUT":
|
|
1094
1109
|
if ("checked" in nextAttrs || "checkedChange" in nextAttrs) _attr_input_checked(scope, nodeAccessor, nextAttrs.checked, nextAttrs.checkedChange);
|
|
@@ -1372,7 +1387,7 @@ function _if(nodeAccessor, ...branchesArgs) {
|
|
|
1372
1387
|
while (i < branchesArgs.length) branches.push(_content("", branchesArgs[i++], branchesArgs[i++], branchesArgs[i++])());
|
|
1373
1388
|
enableBranches();
|
|
1374
1389
|
return (scope, newBranch) => {
|
|
1375
|
-
if (newBranch !== scope[branchAccessor]) setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
|
|
1390
|
+
if (newBranch !== (scope[branchAccessor] ?? (scope["BranchScopes:" + nodeAccessor] && 0))) setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
|
|
1376
1391
|
};
|
|
1377
1392
|
}
|
|
1378
1393
|
function patchDynamicTag(fn) {
|
|
@@ -1464,7 +1479,8 @@ function loop(forEach) {
|
|
|
1464
1479
|
let hasPotentialMoves;
|
|
1465
1480
|
var seenKeys = /* @__PURE__ */ new Set();
|
|
1466
1481
|
forEach(value, (key, args) => {
|
|
1467
|
-
if (
|
|
1482
|
+
if (typeof key !== "string" && typeof key !== "number") console.error(`A <for> tag's \`by\` attribute must return a string or number, but it returned:`, key);
|
|
1483
|
+
else if (seenKeys.has(key)) console.error(`A <for> tag's \`by\` attribute must return a unique value for each item, but a duplicate was found matching:`, key);
|
|
1468
1484
|
else seenKeys.add(key);
|
|
1469
1485
|
let branch = oldLen && (oldScopesByKey ||= oldScopes.reduce((map, scope, i) => map.set(scope["#LoopKey"] ?? i, scope), /* @__PURE__ */ new Map())).get(key);
|
|
1470
1486
|
if (branch) hasPotentialMoves = oldScopesByKey.delete(key);
|
package/dist/debug/html.js
CHANGED
|
@@ -22,6 +22,7 @@ function* attrTagIterator() {
|
|
|
22
22
|
}
|
|
23
23
|
//#endregion
|
|
24
24
|
//#region src/common/errors.ts
|
|
25
|
+
const lowercaseEventHandlerReg = /^on[a-z]/;
|
|
25
26
|
function _el_read_error() {
|
|
26
27
|
throw new Error("Element references can only be read in scripts and event handlers.");
|
|
27
28
|
}
|
|
@@ -46,6 +47,12 @@ function assertExclusiveAttrs(attrs, onError = throwErr) {
|
|
|
46
47
|
if (exclusiveAttrs && exclusiveAttrs.length > 1) onError(`The attributes ${joinWithAnd(exclusiveAttrs)} are mutually exclusive.`);
|
|
47
48
|
}
|
|
48
49
|
}
|
|
50
|
+
function assertValidEventHandlerAttr(name, value) {
|
|
51
|
+
if (value && typeof value !== "string" && lowercaseEventHandlerReg.test(name)) throw new Error(`The \`${name}\` attribute must be a string or a falsey value (\`null\`, \`undefined\`, \`false\`, \`0\`, …), but received type "${typeof value}". To attach an event listener, use the \`on${name[2].toUpperCase()}${name.slice(3)}\` event handler instead.`);
|
|
52
|
+
}
|
|
53
|
+
function assertHandlerIsFunction(name, value) {
|
|
54
|
+
if (value && typeof value !== "function") throw new Error(`The \`${name}\` handler must be a function or a falsey value (\`null\`, \`undefined\`, \`false\`, \`0\`, …), but received type "${typeof value}".`);
|
|
55
|
+
}
|
|
49
56
|
function assertValidTagName(tagName) {
|
|
50
57
|
if (!/^[a-z][a-z0-9._-]*$/i.test(tagName)) throw new Error(`Invalid tag name: "${tagName}". Tag names must start with a letter and contain only letters, numbers, periods, hyphens, and underscores.`);
|
|
51
58
|
}
|
|
@@ -1398,17 +1405,26 @@ function concat(opt, other) {
|
|
|
1398
1405
|
//#region src/html/for.ts
|
|
1399
1406
|
function forOfBy(by, item, index) {
|
|
1400
1407
|
if (by) {
|
|
1401
|
-
|
|
1402
|
-
return
|
|
1408
|
+
const key = typeof by === "string" ? item[by] : by(item, index);
|
|
1409
|
+
if (typeof key !== "string" && typeof key !== "number") console.error(`A <for> tag's \`by\` attribute must return a string or number, but it returned:`, key);
|
|
1410
|
+
return key;
|
|
1403
1411
|
}
|
|
1404
1412
|
return index;
|
|
1405
1413
|
}
|
|
1406
1414
|
function forInBy(by, name, value) {
|
|
1407
|
-
if (by)
|
|
1415
|
+
if (by) {
|
|
1416
|
+
const key = by(name, value);
|
|
1417
|
+
if (typeof key !== "string" && typeof key !== "number") console.error(`A <for> tag's \`by\` attribute must return a string or number, but it returned:`, key);
|
|
1418
|
+
return key;
|
|
1419
|
+
}
|
|
1408
1420
|
return name;
|
|
1409
1421
|
}
|
|
1410
1422
|
function forStepBy(by, index) {
|
|
1411
|
-
if (by)
|
|
1423
|
+
if (by) {
|
|
1424
|
+
const key = by(index);
|
|
1425
|
+
if (typeof key !== "string" && typeof key !== "number") console.error(`A <for> tag's \`by\` attribute must return a string or number, but it returned:`, key);
|
|
1426
|
+
return key;
|
|
1427
|
+
}
|
|
1412
1428
|
return index;
|
|
1413
1429
|
}
|
|
1414
1430
|
//#endregion
|
|
@@ -2414,6 +2430,7 @@ function _attr_nonce() {
|
|
|
2414
2430
|
return getChunk().boundary.state.nonceAttr;
|
|
2415
2431
|
}
|
|
2416
2432
|
function _attr(name, value) {
|
|
2433
|
+
assertValidEventHandlerAttr(name, value);
|
|
2417
2434
|
return isVoid(value) ? "" : nonVoidAttr(name, value);
|
|
2418
2435
|
}
|
|
2419
2436
|
function _attrs(data, nodeAccessor, scopeId, tagName) {
|
|
@@ -2489,6 +2506,7 @@ function _attrs_partial_content(data, skip, nodeAccessor, scopeId, tagName, seri
|
|
|
2489
2506
|
_attr_content(nodeAccessor, scopeId, data?.content, serializeReason);
|
|
2490
2507
|
}
|
|
2491
2508
|
function writeControlledScope(type, scopeId, nodeAccessor, value, valueChange) {
|
|
2509
|
+
assertHandlerIsFunction("valueChange", valueChange);
|
|
2492
2510
|
writeScope(scopeId, {
|
|
2493
2511
|
["ControlledType:" + nodeAccessor]: type,
|
|
2494
2512
|
["ControlledValue:" + nodeAccessor]: value,
|
package/dist/debug/html.mjs
CHANGED
|
@@ -20,6 +20,7 @@ function* attrTagIterator() {
|
|
|
20
20
|
}
|
|
21
21
|
//#endregion
|
|
22
22
|
//#region src/common/errors.ts
|
|
23
|
+
const lowercaseEventHandlerReg = /^on[a-z]/;
|
|
23
24
|
function _el_read_error() {
|
|
24
25
|
throw new Error("Element references can only be read in scripts and event handlers.");
|
|
25
26
|
}
|
|
@@ -44,6 +45,12 @@ function assertExclusiveAttrs(attrs, onError = throwErr) {
|
|
|
44
45
|
if (exclusiveAttrs && exclusiveAttrs.length > 1) onError(`The attributes ${joinWithAnd(exclusiveAttrs)} are mutually exclusive.`);
|
|
45
46
|
}
|
|
46
47
|
}
|
|
48
|
+
function assertValidEventHandlerAttr(name, value) {
|
|
49
|
+
if (value && typeof value !== "string" && lowercaseEventHandlerReg.test(name)) throw new Error(`The \`${name}\` attribute must be a string or a falsey value (\`null\`, \`undefined\`, \`false\`, \`0\`, …), but received type "${typeof value}". To attach an event listener, use the \`on${name[2].toUpperCase()}${name.slice(3)}\` event handler instead.`);
|
|
50
|
+
}
|
|
51
|
+
function assertHandlerIsFunction(name, value) {
|
|
52
|
+
if (value && typeof value !== "function") throw new Error(`The \`${name}\` handler must be a function or a falsey value (\`null\`, \`undefined\`, \`false\`, \`0\`, …), but received type "${typeof value}".`);
|
|
53
|
+
}
|
|
47
54
|
function assertValidTagName(tagName) {
|
|
48
55
|
if (!/^[a-z][a-z0-9._-]*$/i.test(tagName)) throw new Error(`Invalid tag name: "${tagName}". Tag names must start with a letter and contain only letters, numbers, periods, hyphens, and underscores.`);
|
|
49
56
|
}
|
|
@@ -1396,17 +1403,26 @@ function concat(opt, other) {
|
|
|
1396
1403
|
//#region src/html/for.ts
|
|
1397
1404
|
function forOfBy(by, item, index) {
|
|
1398
1405
|
if (by) {
|
|
1399
|
-
|
|
1400
|
-
return
|
|
1406
|
+
const key = typeof by === "string" ? item[by] : by(item, index);
|
|
1407
|
+
if (typeof key !== "string" && typeof key !== "number") console.error(`A <for> tag's \`by\` attribute must return a string or number, but it returned:`, key);
|
|
1408
|
+
return key;
|
|
1401
1409
|
}
|
|
1402
1410
|
return index;
|
|
1403
1411
|
}
|
|
1404
1412
|
function forInBy(by, name, value) {
|
|
1405
|
-
if (by)
|
|
1413
|
+
if (by) {
|
|
1414
|
+
const key = by(name, value);
|
|
1415
|
+
if (typeof key !== "string" && typeof key !== "number") console.error(`A <for> tag's \`by\` attribute must return a string or number, but it returned:`, key);
|
|
1416
|
+
return key;
|
|
1417
|
+
}
|
|
1406
1418
|
return name;
|
|
1407
1419
|
}
|
|
1408
1420
|
function forStepBy(by, index) {
|
|
1409
|
-
if (by)
|
|
1421
|
+
if (by) {
|
|
1422
|
+
const key = by(index);
|
|
1423
|
+
if (typeof key !== "string" && typeof key !== "number") console.error(`A <for> tag's \`by\` attribute must return a string or number, but it returned:`, key);
|
|
1424
|
+
return key;
|
|
1425
|
+
}
|
|
1410
1426
|
return index;
|
|
1411
1427
|
}
|
|
1412
1428
|
//#endregion
|
|
@@ -2412,6 +2428,7 @@ function _attr_nonce() {
|
|
|
2412
2428
|
return getChunk().boundary.state.nonceAttr;
|
|
2413
2429
|
}
|
|
2414
2430
|
function _attr(name, value) {
|
|
2431
|
+
assertValidEventHandlerAttr(name, value);
|
|
2415
2432
|
return isVoid(value) ? "" : nonVoidAttr(name, value);
|
|
2416
2433
|
}
|
|
2417
2434
|
function _attrs(data, nodeAccessor, scopeId, tagName) {
|
|
@@ -2487,6 +2504,7 @@ function _attrs_partial_content(data, skip, nodeAccessor, scopeId, tagName, seri
|
|
|
2487
2504
|
_attr_content(nodeAccessor, scopeId, data?.content, serializeReason);
|
|
2488
2505
|
}
|
|
2489
2506
|
function writeControlledScope(type, scopeId, nodeAccessor, value, valueChange) {
|
|
2507
|
+
assertHandlerIsFunction("valueChange", valueChange);
|
|
2490
2508
|
writeScope(scopeId, {
|
|
2491
2509
|
["ControlledType:" + nodeAccessor]: type,
|
|
2492
2510
|
["ControlledValue:" + nodeAccessor]: value,
|
package/dist/dom.js
CHANGED
|
@@ -770,7 +770,8 @@ function _attrs_partial_content(scope, nodeAccessor, nextAttrs, skip) {
|
|
|
770
770
|
_attrs_partial(scope, nodeAccessor, nextAttrs, skip), _attr_content(scope, nodeAccessor, nextAttrs?.content);
|
|
771
771
|
}
|
|
772
772
|
function attrsInternal(scope, nodeAccessor, nextAttrs) {
|
|
773
|
-
let el = scope[nodeAccessor], events, skip;
|
|
773
|
+
let el = scope[nodeAccessor], events = scope["I" + nodeAccessor], skip;
|
|
774
|
+
for (let name in events) events[name] = 0;
|
|
774
775
|
switch (el.tagName) {
|
|
775
776
|
case "INPUT":
|
|
776
777
|
if ("checked" in nextAttrs || "checkedChange" in nextAttrs) _attr_input_checked(scope, nodeAccessor, nextAttrs.checked, nextAttrs.checkedChange);
|
|
@@ -955,7 +956,7 @@ function _if(nodeAccessor, ...branchesArgs) {
|
|
|
955
956
|
let branchAccessor = "D" + nodeAccessor, branches = [], i = 0;
|
|
956
957
|
for (; i < branchesArgs.length;) branches.push(_content("", branchesArgs[i++], branchesArgs[i++], branchesArgs[i++])());
|
|
957
958
|
return enableBranches(), (scope, newBranch) => {
|
|
958
|
-
newBranch !== scope[branchAccessor] && setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
|
|
959
|
+
newBranch !== (scope[branchAccessor] ?? (scope["A" + nodeAccessor] && 0)) && setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
|
|
959
960
|
};
|
|
960
961
|
}
|
|
961
962
|
function patchDynamicTag(fn) {
|
package/dist/dom.mjs
CHANGED
|
@@ -769,7 +769,8 @@ function _attrs_partial_content(scope, nodeAccessor, nextAttrs, skip) {
|
|
|
769
769
|
_attrs_partial(scope, nodeAccessor, nextAttrs, skip), _attr_content(scope, nodeAccessor, nextAttrs?.content);
|
|
770
770
|
}
|
|
771
771
|
function attrsInternal(scope, nodeAccessor, nextAttrs) {
|
|
772
|
-
let el = scope[nodeAccessor], events, skip;
|
|
772
|
+
let el = scope[nodeAccessor], events = scope["I" + nodeAccessor], skip;
|
|
773
|
+
for (let name in events) events[name] = 0;
|
|
773
774
|
switch (el.tagName) {
|
|
774
775
|
case "INPUT":
|
|
775
776
|
if ("checked" in nextAttrs || "checkedChange" in nextAttrs) _attr_input_checked(scope, nodeAccessor, nextAttrs.checked, nextAttrs.checkedChange);
|
|
@@ -954,7 +955,7 @@ function _if(nodeAccessor, ...branchesArgs) {
|
|
|
954
955
|
let branchAccessor = "D" + nodeAccessor, branches = [], i = 0;
|
|
955
956
|
for (; i < branchesArgs.length;) branches.push(_content("", branchesArgs[i++], branchesArgs[i++], branchesArgs[i++])());
|
|
956
957
|
return enableBranches(), (scope, newBranch) => {
|
|
957
|
-
newBranch !== scope[branchAccessor] && setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
|
|
958
|
+
newBranch !== (scope[branchAccessor] ?? (scope["A" + nodeAccessor] && 0)) && setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
|
|
958
959
|
};
|
|
959
960
|
}
|
|
960
961
|
function patchDynamicTag(fn) {
|
package/dist/translator/index.js
CHANGED
|
@@ -182,15 +182,15 @@ function isNullableExpr(expr) {
|
|
|
182
182
|
case "**=": return false;
|
|
183
183
|
case "||=":
|
|
184
184
|
case "??=": return isNullableExpr(expr.right) || isNullableExpr(expr.left);
|
|
185
|
-
case "&&=": return isNullableExpr(expr.left)
|
|
185
|
+
case "&&=": return isNullableExpr(expr.left) || isNullableExpr(expr.right);
|
|
186
186
|
default: return true;
|
|
187
187
|
}
|
|
188
188
|
case "AwaitExpression": return isNullableExpr(expr.argument);
|
|
189
|
-
case "ConditionalExpression": return isNullableExpr(expr.consequent)
|
|
189
|
+
case "ConditionalExpression": return isNullableExpr(expr.consequent) || isNullableExpr(expr.alternate);
|
|
190
190
|
case "LogicalExpression": switch (expr.operator) {
|
|
191
191
|
case "||":
|
|
192
192
|
case "??": return isNullableExpr(expr.right) || isNullableExpr(expr.left);
|
|
193
|
-
case "&&": return isNullableExpr(expr.left)
|
|
193
|
+
case "&&": return isNullableExpr(expr.left) || isNullableExpr(expr.right);
|
|
194
194
|
default: return true;
|
|
195
195
|
}
|
|
196
196
|
case "ParenthesizedExpression": return isNullableExpr(expr.expression);
|
|
@@ -2807,7 +2807,7 @@ function simplifyFunction(fn) {
|
|
|
2807
2807
|
case "FunctionDeclaration":
|
|
2808
2808
|
case "FunctionExpression":
|
|
2809
2809
|
case "ArrowFunctionExpression": return fn;
|
|
2810
|
-
default: return _marko_compiler.types.functionExpression(null, fn.params, fn.body, fn.
|
|
2810
|
+
default: return _marko_compiler.types.functionExpression(null, fn.params, fn.body, fn.generator, fn.async);
|
|
2811
2811
|
}
|
|
2812
2812
|
}
|
|
2813
2813
|
//#endregion
|
|
@@ -4181,10 +4181,14 @@ var native_tag_default = {
|
|
|
4181
4181
|
}
|
|
4182
4182
|
seen[attr.name] = attr;
|
|
4183
4183
|
if (injectNonce && attr.name === "nonce") injectNonce = false;
|
|
4184
|
+
if (isEventOrChangeHandler(attr.name)) assertNativeHandlerAttr(tag, attr);
|
|
4184
4185
|
if (isEventHandler(attr.name)) {
|
|
4185
4186
|
valueExtra.isEffect = true;
|
|
4186
4187
|
hasEventHandlers = true;
|
|
4187
|
-
} else
|
|
4188
|
+
} else {
|
|
4189
|
+
assertValidNativeEventHandlerAttr(tag, attr);
|
|
4190
|
+
if (!evaluate(attr.value).confident) hasDynamicAttributes = true;
|
|
4191
|
+
}
|
|
4188
4192
|
} else if (_marko_compiler.types.isMarkoSpreadAttribute(attr)) {
|
|
4189
4193
|
valueExtra.isEffect = true;
|
|
4190
4194
|
hasEventHandlers = true;
|
|
@@ -4593,6 +4597,23 @@ function isInjectNonceTag(tagName) {
|
|
|
4593
4597
|
default: return false;
|
|
4594
4598
|
}
|
|
4595
4599
|
}
|
|
4600
|
+
function assertNativeHandlerAttr(tag, attr) {
|
|
4601
|
+
if ((0, _marko_compiler_babel_utils.computeNode)(attr.value)?.value) throw tag.hub.buildError(attr.value, `The \`${attr.name}\` ${isEventHandler(attr.name) ? "event handler" : "change handler"} on a [native tag](https://markojs.com/docs/reference/native-tag) must be a function or a falsey value (\`null\`, \`undefined\`, \`false\`, \`0\`, …).`, Error);
|
|
4602
|
+
}
|
|
4603
|
+
const lowercaseEventHandlerReg = /^on[a-z]/;
|
|
4604
|
+
function assertValidNativeEventHandlerAttr(tag, attr) {
|
|
4605
|
+
if (!lowercaseEventHandlerReg.test(attr.name)) return;
|
|
4606
|
+
const { value } = attr;
|
|
4607
|
+
let invalid = _marko_compiler.types.isFunction(value);
|
|
4608
|
+
if (!invalid) {
|
|
4609
|
+
const { confident, computed } = evaluate(value);
|
|
4610
|
+
invalid = confident && !!computed && typeof computed !== "string";
|
|
4611
|
+
}
|
|
4612
|
+
if (invalid) {
|
|
4613
|
+
const suggestion = "on" + attr.name[2].toUpperCase() + attr.name.slice(3);
|
|
4614
|
+
throw tag.hub.buildError(value, `The \`${attr.name}\` attribute on a [native tag](https://markojs.com/docs/reference/native-tag) must be a string or a falsey value (\`null\`, \`undefined\`, \`false\`, \`0\`, …). To attach an event listener, use the \`${suggestion}\` [event handler attribute](https://markojs.com/docs/reference/event-handling) instead.`, Error);
|
|
4615
|
+
}
|
|
4616
|
+
}
|
|
4596
4617
|
function getCanonicalTagName(tag) {
|
|
4597
4618
|
const tagName = getTagName(tag);
|
|
4598
4619
|
switch (tagName) {
|