@marko/runtime-tags 6.1.12 → 6.1.14
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 +5 -0
- package/dist/common/helpers.d.ts +1 -0
- package/dist/debug/dom.js +152 -55
- package/dist/debug/dom.mjs +152 -55
- package/dist/debug/html.js +176 -76
- package/dist/debug/html.mjs +176 -76
- package/dist/dom.js +24 -23
- package/dist/dom.mjs +24 -23
- package/dist/html/writer.d.ts +1 -1
- package/dist/html.js +51 -44
- package/dist/html.mjs +51 -44
- package/dist/translator/index.js +89 -9
- package/dist/translator/util/normalize-string-expression.d.ts +1 -0
- package/package.json +1 -1
package/dist/common/errors.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
export declare function assertValidAttrValue(name: string, value: unknown): void;
|
|
2
|
+
export declare function assertValidTextValue(value: unknown): void;
|
|
3
|
+
export declare function assertValidLoopKey(key: unknown, seenKeys?: Set<unknown>): void;
|
|
4
|
+
export declare function assertValidAttrName(name: string): void;
|
|
1
5
|
export declare function _el_read_error(): void;
|
|
2
6
|
export declare function _hoist_read_error(): void;
|
|
3
7
|
export declare function _assert_hoist(value: unknown): void;
|
|
4
8
|
export declare function assertExclusiveAttrs(attrs: Record<string, unknown> | undefined, onError?: typeof throwErr): void;
|
|
9
|
+
export declare function assertHandlerIsFunction(name: string, value: unknown): void;
|
|
5
10
|
export declare function assertValidTagName(tagName: string): void;
|
|
6
11
|
declare function throwErr(msg: string): void;
|
|
7
12
|
export {};
|
package/dist/common/helpers.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const htmlAttrNameReg: RegExp;
|
|
2
2
|
export declare const userAttrNameReg: RegExp;
|
|
3
|
+
export declare function getWrongAttrSuggestion(name: string): string | undefined;
|
|
3
4
|
export declare function _call<T>(fn: (v: T) => unknown, v: T): T;
|
|
4
5
|
export declare function stringifyClassObject(name: string, value: unknown): string;
|
|
5
6
|
export declare function stringifyStyleObject(name: string, value: unknown): string;
|
package/dist/debug/dom.js
CHANGED
|
@@ -21,7 +21,133 @@ function* attrTagIterator() {
|
|
|
21
21
|
yield* this[rest];
|
|
22
22
|
}
|
|
23
23
|
//#endregion
|
|
24
|
+
//#region src/common/helpers.ts
|
|
25
|
+
const htmlAttrNameReg = /^[^a-z_]|[^a-z0-9._:-]/i;
|
|
26
|
+
const knownWrongAttrs = {
|
|
27
|
+
className: "class",
|
|
28
|
+
classList: "class",
|
|
29
|
+
htmlFor: "for",
|
|
30
|
+
acceptCharset: "accept-charset",
|
|
31
|
+
httpEquiv: "http-equiv",
|
|
32
|
+
defaultValue: "value",
|
|
33
|
+
defaultChecked: "checked",
|
|
34
|
+
dangerouslySetInnerHTML: "$!{html}",
|
|
35
|
+
key: "<for by>",
|
|
36
|
+
ref: "<tag/ref>",
|
|
37
|
+
"v-if": "<if>",
|
|
38
|
+
"v-else": "<else>",
|
|
39
|
+
"v-else-if": "<else if>",
|
|
40
|
+
"v-for": "<for>",
|
|
41
|
+
"v-show": "<if>",
|
|
42
|
+
"v-model": "value:=state",
|
|
43
|
+
"v-bind": "...attrs",
|
|
44
|
+
"v-html": "$!{html}",
|
|
45
|
+
"v-text": "${text}"
|
|
46
|
+
};
|
|
47
|
+
function getWrongAttrSuggestion(name) {
|
|
48
|
+
const exact = knownWrongAttrs[name];
|
|
49
|
+
if (exact) return exact;
|
|
50
|
+
const colon = name.indexOf(":");
|
|
51
|
+
if (colon > 0) {
|
|
52
|
+
const rest = name.slice(colon + 1);
|
|
53
|
+
switch (name.slice(0, colon)) {
|
|
54
|
+
case "class": return `class={ ${rest}: condition }`;
|
|
55
|
+
case "style": return `style={ ${rest}: value }`;
|
|
56
|
+
case "on":
|
|
57
|
+
case "v-on": return `on${rest.charAt(0).toUpperCase()}${rest.slice(1)}`;
|
|
58
|
+
case "bind":
|
|
59
|
+
case "v-model": return `${rest}:=state`;
|
|
60
|
+
case "v-bind": return rest;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function _call(fn, v) {
|
|
65
|
+
fn(v);
|
|
66
|
+
return v;
|
|
67
|
+
}
|
|
68
|
+
function stringifyClassObject(name, value) {
|
|
69
|
+
return value ? name : "";
|
|
70
|
+
}
|
|
71
|
+
function stringifyStyleObject(name, value) {
|
|
72
|
+
return value || value === 0 ? name + ":" + value : "";
|
|
73
|
+
}
|
|
74
|
+
const toDelimitedString = function toDelimitedString(val, delimiter, stringify) {
|
|
75
|
+
let str = "";
|
|
76
|
+
let sep = "";
|
|
77
|
+
let part;
|
|
78
|
+
if (val) if (typeof val !== "object") str += val;
|
|
79
|
+
else if (Array.isArray(val)) for (const v of val) {
|
|
80
|
+
part = toDelimitedString(v, delimiter, stringify);
|
|
81
|
+
if (part) {
|
|
82
|
+
str += sep + part;
|
|
83
|
+
sep = delimiter;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else for (const name in val) {
|
|
87
|
+
part = stringify(name, val[name]);
|
|
88
|
+
if (part) {
|
|
89
|
+
str += sep + part;
|
|
90
|
+
sep = delimiter;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return str;
|
|
94
|
+
};
|
|
95
|
+
function isEventHandler(name) {
|
|
96
|
+
return /^on[A-Z-]/.test(name);
|
|
97
|
+
}
|
|
98
|
+
function getEventHandlerName(name) {
|
|
99
|
+
return name[2] === "-" ? name.slice(3) : name.slice(2).toLowerCase();
|
|
100
|
+
}
|
|
101
|
+
function isNotVoid(value) {
|
|
102
|
+
return value != null && value !== false;
|
|
103
|
+
}
|
|
104
|
+
function normalizeDynamicRenderer(value) {
|
|
105
|
+
if (value) {
|
|
106
|
+
if (typeof value === "string") return value;
|
|
107
|
+
const normalized = value.content || value.default || value;
|
|
108
|
+
if ("id" in normalized) return normalized;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
24
112
|
//#region src/common/errors.ts
|
|
113
|
+
const lowercaseEventHandlerReg = /^on[a-z]/;
|
|
114
|
+
function assertValidAttrValue(name, value) {
|
|
115
|
+
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.`);
|
|
116
|
+
if (typeof value === "function") {
|
|
117
|
+
if (name === "content" || /^on/i.test(name) || /Change$/.test(name)) return;
|
|
118
|
+
throw new Error(`The \`${name}\` attribute cannot be a function.`);
|
|
119
|
+
}
|
|
120
|
+
const unrenderable = describeUnrenderable(value);
|
|
121
|
+
if (unrenderable) throw new Error(`The \`${name}\` attribute cannot be ${unrenderable}.`);
|
|
122
|
+
}
|
|
123
|
+
function assertValidTextValue(value) {
|
|
124
|
+
const unrenderable = describeUnrenderable(value);
|
|
125
|
+
if (unrenderable) throw new Error(`Text content cannot be ${unrenderable}.`);
|
|
126
|
+
}
|
|
127
|
+
function describeUnrenderable(value) {
|
|
128
|
+
if (typeof value === "symbol") return "a symbol";
|
|
129
|
+
if (typeof value === "object" && value !== null) {
|
|
130
|
+
let stringified;
|
|
131
|
+
try {
|
|
132
|
+
stringified = `${value}`;
|
|
133
|
+
} catch {
|
|
134
|
+
stringified = "[object Object]";
|
|
135
|
+
}
|
|
136
|
+
if (/^\[object \w+\]$/.test(stringified)) return stringified === "[object Promise]" ? "a promise (use the `<await>` tag to render its resolved value)" : stringified === "[object Object]" ? "a plain object (it would render as `[object Object]`)" : `a value that renders as \`${stringified}\``;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function assertValidLoopKey(key, seenKeys) {
|
|
140
|
+
if (typeof key !== "string" && typeof key !== "number") throw new Error(`A \`<for>\` tag's \`by\` attribute must return a string or number for each item, but received ${key === null ? "null" : `type "${typeof key}"`}.`);
|
|
141
|
+
if (seenKeys) {
|
|
142
|
+
if (seenKeys.has(key)) throw new Error(`A \`<for>\` tag's \`by\` attribute must return a unique value for each item, but \`${key}\` was used more than once.`);
|
|
143
|
+
seenKeys.add(key);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function assertValidAttrName(name) {
|
|
147
|
+
if (htmlAttrNameReg.test(name)) throw new Error(`Invalid attribute name: ${JSON.stringify(name)}`);
|
|
148
|
+
const suggestion = getWrongAttrSuggestion(name);
|
|
149
|
+
if (suggestion) throw new Error(`\`${name}\` is not a valid attribute, did you mean \`${suggestion}\`?`);
|
|
150
|
+
}
|
|
25
151
|
function _el_read_error() {
|
|
26
152
|
throw new Error("Element references can only be read in scripts and event handlers.");
|
|
27
153
|
}
|
|
@@ -46,6 +172,9 @@ function assertExclusiveAttrs(attrs, onError = throwErr) {
|
|
|
46
172
|
if (exclusiveAttrs && exclusiveAttrs.length > 1) onError(`The attributes ${joinWithAnd(exclusiveAttrs)} are mutually exclusive.`);
|
|
47
173
|
}
|
|
48
174
|
}
|
|
175
|
+
function assertHandlerIsFunction(name, value) {
|
|
176
|
+
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}".`);
|
|
177
|
+
}
|
|
49
178
|
function assertValidTagName(tagName) {
|
|
50
179
|
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
180
|
}
|
|
@@ -82,56 +211,6 @@ function forUntil(until, from, step, cb) {
|
|
|
82
211
|
for (let steps = (until - start) / delta, i = 0; i < steps; i++) cb(start + i * delta);
|
|
83
212
|
}
|
|
84
213
|
//#endregion
|
|
85
|
-
//#region src/common/helpers.ts
|
|
86
|
-
const htmlAttrNameReg = /^[^a-z_]|[^a-z0-9._:-]/i;
|
|
87
|
-
function _call(fn, v) {
|
|
88
|
-
fn(v);
|
|
89
|
-
return v;
|
|
90
|
-
}
|
|
91
|
-
function stringifyClassObject(name, value) {
|
|
92
|
-
return value ? name : "";
|
|
93
|
-
}
|
|
94
|
-
function stringifyStyleObject(name, value) {
|
|
95
|
-
return value || value === 0 ? name + ":" + value : "";
|
|
96
|
-
}
|
|
97
|
-
const toDelimitedString = function toDelimitedString(val, delimiter, stringify) {
|
|
98
|
-
let str = "";
|
|
99
|
-
let sep = "";
|
|
100
|
-
let part;
|
|
101
|
-
if (val) if (typeof val !== "object") str += val;
|
|
102
|
-
else if (Array.isArray(val)) for (const v of val) {
|
|
103
|
-
part = toDelimitedString(v, delimiter, stringify);
|
|
104
|
-
if (part) {
|
|
105
|
-
str += sep + part;
|
|
106
|
-
sep = delimiter;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
else for (const name in val) {
|
|
110
|
-
part = stringify(name, val[name]);
|
|
111
|
-
if (part) {
|
|
112
|
-
str += sep + part;
|
|
113
|
-
sep = delimiter;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return str;
|
|
117
|
-
};
|
|
118
|
-
function isEventHandler(name) {
|
|
119
|
-
return /^on[A-Z-]/.test(name);
|
|
120
|
-
}
|
|
121
|
-
function getEventHandlerName(name) {
|
|
122
|
-
return name[2] === "-" ? name.slice(3) : name.slice(2).toLowerCase();
|
|
123
|
-
}
|
|
124
|
-
function isNotVoid(value) {
|
|
125
|
-
return value != null && value !== false;
|
|
126
|
-
}
|
|
127
|
-
function normalizeDynamicRenderer(value) {
|
|
128
|
-
if (value) {
|
|
129
|
-
if (typeof value === "string") return value;
|
|
130
|
-
const normalized = value.content || value.default || value;
|
|
131
|
-
if ("id" in normalized) return normalized;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
//#endregion
|
|
135
214
|
//#region src/common/meta.ts
|
|
136
215
|
const DYNAMIC_TAG_SCRIPT_REGISTER_ID = "_dynamicTagScript";
|
|
137
216
|
//#endregion
|
|
@@ -146,6 +225,7 @@ function push(opt, item) {
|
|
|
146
225
|
//#region src/dom/event.ts
|
|
147
226
|
const defaultDelegator = /* @__PURE__ */ createDelegator();
|
|
148
227
|
function _on(element, type, handler) {
|
|
228
|
+
assertHandlerIsFunction("on" + type[0].toUpperCase() + type.slice(1), handler);
|
|
149
229
|
if (element["$" + type] === void 0) defaultDelegator(element, type, handleDelegated);
|
|
150
230
|
element["$" + type] = handler || null;
|
|
151
231
|
}
|
|
@@ -793,6 +873,7 @@ function _attr_input_checked_default(scope, nodeAccessor, checked) {
|
|
|
793
873
|
function _attr_input_checked(scope, nodeAccessor, checked, checkedChange) {
|
|
794
874
|
const el = scope[nodeAccessor];
|
|
795
875
|
const normalizedChecked = isNotVoid(checked);
|
|
876
|
+
assertHandlerIsFunction("checkedChange", checkedChange);
|
|
796
877
|
scope["ControlledHandler:" + nodeAccessor] = checkedChange;
|
|
797
878
|
scope["ControlledType:" + nodeAccessor] = checkedChange ? 0 : 5;
|
|
798
879
|
if (checkedChange && scope["#Gen"] < runId) el.checked = normalizedChecked;
|
|
@@ -821,6 +902,7 @@ function _attr_input_checkedValue(scope, nodeAccessor, checkedValue, checkedValu
|
|
|
821
902
|
const el = scope[nodeAccessor];
|
|
822
903
|
const multiple = Array.isArray(checkedValue);
|
|
823
904
|
const normalizedCheckedValue = scope["ControlledValue:" + nodeAccessor] = multiple ? checkedValue.map(normalizeStrProp) : normalizeStrProp(checkedValue);
|
|
905
|
+
assertHandlerIsFunction("checkedValueChange", checkedValueChange);
|
|
824
906
|
scope["ControlledHandler:" + nodeAccessor] = checkedValueChange;
|
|
825
907
|
scope["ControlledType:" + nodeAccessor] = checkedValueChange ? 1 : 5;
|
|
826
908
|
if (checkedValueChange && scope["#Gen"] < runId) {
|
|
@@ -857,6 +939,7 @@ function _attr_input_value_default(scope, nodeAccessor, value) {
|
|
|
857
939
|
function _attr_input_value(scope, nodeAccessor, value, valueChange) {
|
|
858
940
|
const el = scope[nodeAccessor];
|
|
859
941
|
const normalizedValue = normalizeAttrValue(value) || "";
|
|
942
|
+
assertHandlerIsFunction("valueChange", valueChange);
|
|
860
943
|
scope["ControlledHandler:" + nodeAccessor] = valueChange;
|
|
861
944
|
scope["ControlledValue:" + nodeAccessor] = normalizedValue;
|
|
862
945
|
scope["ControlledType:" + nodeAccessor] = valueChange ? 2 : 5;
|
|
@@ -905,8 +988,10 @@ function _attr_select_value(scope, nodeAccessor, value, valueChange) {
|
|
|
905
988
|
const existing = scope["#Gen"] < runId;
|
|
906
989
|
const multiple = Array.isArray(value);
|
|
907
990
|
const normalizedValue = scope["ControlledValue:" + nodeAccessor] = multiple ? value.map(normalizeStrProp) : normalizeStrProp(value);
|
|
991
|
+
assertHandlerIsFunction("valueChange", valueChange);
|
|
908
992
|
scope["ControlledHandler:" + nodeAccessor] = valueChange;
|
|
909
993
|
scope["ControlledType:" + nodeAccessor] = valueChange ? 3 : 5;
|
|
994
|
+
if (valueChange) pendingEffects.unshift(() => assertSelectValueMatchesOption(el, normalizedValue, value), scope);
|
|
910
995
|
if (valueChange && existing) pendingEffects.unshift(() => setSelectValue(el, normalizedValue, multiple), scope);
|
|
911
996
|
else _attr_select_value_default(scope, nodeAccessor, normalizedValue);
|
|
912
997
|
}
|
|
@@ -949,11 +1034,19 @@ function setSelectValue(el, value, multiple) {
|
|
|
949
1034
|
function getSelectValue(el, multiple) {
|
|
950
1035
|
return multiple ? Array.from(el.selectedOptions, (opt) => opt.value) : el.value;
|
|
951
1036
|
}
|
|
1037
|
+
function assertSelectValueMatchesOption(el, normalizedValue, value) {
|
|
1038
|
+
const multiple = Array.isArray(normalizedValue);
|
|
1039
|
+
if (multiple ? normalizedValue.some(Boolean) : normalizedValue) {
|
|
1040
|
+
for (const opt of el.options) if (multiple ? normalizedValue.includes(opt.value) : opt.value === normalizedValue) return;
|
|
1041
|
+
console.error("A controlled `<select>`'s `value` has no matching `<option>`:", value);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
952
1044
|
function _attr_details_or_dialog_open_default(scope, nodeAccessor, open) {
|
|
953
1045
|
if (scope["#Gen"] === runId) scope[nodeAccessor].open = isNotVoid(open);
|
|
954
1046
|
}
|
|
955
1047
|
function _attr_details_or_dialog_open(scope, nodeAccessor, open, openChange) {
|
|
956
1048
|
const normalizedOpen = scope["ControlledValue:" + nodeAccessor] = isNotVoid(open);
|
|
1049
|
+
assertHandlerIsFunction("openChange", openChange);
|
|
957
1050
|
scope["ControlledHandler:" + nodeAccessor] = openChange;
|
|
958
1051
|
scope["ControlledType:" + nodeAccessor] = openChange ? 4 : 5;
|
|
959
1052
|
if (openChange && scope["#Gen"] < runId) scope[nodeAccessor].open = normalizedOpen;
|
|
@@ -1012,9 +1105,11 @@ function updateList(arr, val, push) {
|
|
|
1012
1105
|
//#endregion
|
|
1013
1106
|
//#region src/dom/dom.ts
|
|
1014
1107
|
function _to_text(value) {
|
|
1108
|
+
assertValidTextValue(value);
|
|
1015
1109
|
return value || value === 0 ? value + "" : "";
|
|
1016
1110
|
}
|
|
1017
1111
|
function _attr(element, name, value) {
|
|
1112
|
+
assertValidAttrValue(name, value);
|
|
1018
1113
|
setAttribute(element, name, normalizeAttrValue(value));
|
|
1019
1114
|
}
|
|
1020
1115
|
function setAttribute(element, name, value) {
|
|
@@ -1089,8 +1184,11 @@ function _attrs_partial_content(scope, nodeAccessor, nextAttrs, skip) {
|
|
|
1089
1184
|
}
|
|
1090
1185
|
function attrsInternal(scope, nodeAccessor, nextAttrs) {
|
|
1091
1186
|
const el = scope[nodeAccessor];
|
|
1092
|
-
let events;
|
|
1187
|
+
let events = scope["EventAttributes:" + nodeAccessor];
|
|
1093
1188
|
let skip;
|
|
1189
|
+
for (const name in events) events[name] = 0;
|
|
1190
|
+
scope["ControlledType:" + nodeAccessor] = 5;
|
|
1191
|
+
scope["ControlledHandler:" + nodeAccessor] = 0;
|
|
1094
1192
|
switch (el.tagName) {
|
|
1095
1193
|
case "INPUT":
|
|
1096
1194
|
if ("checked" in nextAttrs || "checkedChange" in nextAttrs) _attr_input_checked(scope, nodeAccessor, nextAttrs.checked, nextAttrs.checkedChange);
|
|
@@ -1129,7 +1227,7 @@ function attrsInternal(scope, nodeAccessor, nextAttrs) {
|
|
|
1129
1227
|
_attr_style(el, value);
|
|
1130
1228
|
break;
|
|
1131
1229
|
default:
|
|
1132
|
-
|
|
1230
|
+
assertValidAttrName(name);
|
|
1133
1231
|
if (isEventHandler(name)) (events ||= scope["EventAttributes:" + nodeAccessor] = {})[getEventHandlerName(name)] = value;
|
|
1134
1232
|
else if (!(skip?.test(name) || name === "content" && el.tagName !== "META")) _attr(el, name, value);
|
|
1135
1233
|
break;
|
|
@@ -1374,7 +1472,7 @@ function _if(nodeAccessor, ...branchesArgs) {
|
|
|
1374
1472
|
while (i < branchesArgs.length) branches.push(_content("", branchesArgs[i++], branchesArgs[i++], branchesArgs[i++])());
|
|
1375
1473
|
enableBranches();
|
|
1376
1474
|
return (scope, newBranch) => {
|
|
1377
|
-
if (newBranch !== scope[branchAccessor]) setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
|
|
1475
|
+
if (newBranch !== (scope[branchAccessor] ?? (scope["BranchScopes:" + nodeAccessor] && 0))) setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
|
|
1378
1476
|
};
|
|
1379
1477
|
}
|
|
1380
1478
|
function patchDynamicTag(fn) {
|
|
@@ -1466,8 +1564,7 @@ function loop(forEach) {
|
|
|
1466
1564
|
let hasPotentialMoves;
|
|
1467
1565
|
var seenKeys = /* @__PURE__ */ new Set();
|
|
1468
1566
|
forEach(value, (key, args) => {
|
|
1469
|
-
|
|
1470
|
-
else seenKeys.add(key);
|
|
1567
|
+
assertValidLoopKey(key, seenKeys);
|
|
1471
1568
|
let branch = oldLen && (oldScopesByKey ||= oldScopes.reduce((map, scope, i) => map.set(scope["#LoopKey"] ?? i, scope), /* @__PURE__ */ new Map())).get(key);
|
|
1472
1569
|
if (branch) hasPotentialMoves = oldScopesByKey.delete(key);
|
|
1473
1570
|
else branch = createAndSetupBranch(scope["$global"], renderer, scope, parentNode);
|
package/dist/debug/dom.mjs
CHANGED
|
@@ -19,7 +19,133 @@ function* attrTagIterator() {
|
|
|
19
19
|
yield* this[rest];
|
|
20
20
|
}
|
|
21
21
|
//#endregion
|
|
22
|
+
//#region src/common/helpers.ts
|
|
23
|
+
const htmlAttrNameReg = /^[^a-z_]|[^a-z0-9._:-]/i;
|
|
24
|
+
const knownWrongAttrs = {
|
|
25
|
+
className: "class",
|
|
26
|
+
classList: "class",
|
|
27
|
+
htmlFor: "for",
|
|
28
|
+
acceptCharset: "accept-charset",
|
|
29
|
+
httpEquiv: "http-equiv",
|
|
30
|
+
defaultValue: "value",
|
|
31
|
+
defaultChecked: "checked",
|
|
32
|
+
dangerouslySetInnerHTML: "$!{html}",
|
|
33
|
+
key: "<for by>",
|
|
34
|
+
ref: "<tag/ref>",
|
|
35
|
+
"v-if": "<if>",
|
|
36
|
+
"v-else": "<else>",
|
|
37
|
+
"v-else-if": "<else if>",
|
|
38
|
+
"v-for": "<for>",
|
|
39
|
+
"v-show": "<if>",
|
|
40
|
+
"v-model": "value:=state",
|
|
41
|
+
"v-bind": "...attrs",
|
|
42
|
+
"v-html": "$!{html}",
|
|
43
|
+
"v-text": "${text}"
|
|
44
|
+
};
|
|
45
|
+
function getWrongAttrSuggestion(name) {
|
|
46
|
+
const exact = knownWrongAttrs[name];
|
|
47
|
+
if (exact) return exact;
|
|
48
|
+
const colon = name.indexOf(":");
|
|
49
|
+
if (colon > 0) {
|
|
50
|
+
const rest = name.slice(colon + 1);
|
|
51
|
+
switch (name.slice(0, colon)) {
|
|
52
|
+
case "class": return `class={ ${rest}: condition }`;
|
|
53
|
+
case "style": return `style={ ${rest}: value }`;
|
|
54
|
+
case "on":
|
|
55
|
+
case "v-on": return `on${rest.charAt(0).toUpperCase()}${rest.slice(1)}`;
|
|
56
|
+
case "bind":
|
|
57
|
+
case "v-model": return `${rest}:=state`;
|
|
58
|
+
case "v-bind": return rest;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function _call(fn, v) {
|
|
63
|
+
fn(v);
|
|
64
|
+
return v;
|
|
65
|
+
}
|
|
66
|
+
function stringifyClassObject(name, value) {
|
|
67
|
+
return value ? name : "";
|
|
68
|
+
}
|
|
69
|
+
function stringifyStyleObject(name, value) {
|
|
70
|
+
return value || value === 0 ? name + ":" + value : "";
|
|
71
|
+
}
|
|
72
|
+
const toDelimitedString = function toDelimitedString(val, delimiter, stringify) {
|
|
73
|
+
let str = "";
|
|
74
|
+
let sep = "";
|
|
75
|
+
let part;
|
|
76
|
+
if (val) if (typeof val !== "object") str += val;
|
|
77
|
+
else if (Array.isArray(val)) for (const v of val) {
|
|
78
|
+
part = toDelimitedString(v, delimiter, stringify);
|
|
79
|
+
if (part) {
|
|
80
|
+
str += sep + part;
|
|
81
|
+
sep = delimiter;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else for (const name in val) {
|
|
85
|
+
part = stringify(name, val[name]);
|
|
86
|
+
if (part) {
|
|
87
|
+
str += sep + part;
|
|
88
|
+
sep = delimiter;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return str;
|
|
92
|
+
};
|
|
93
|
+
function isEventHandler(name) {
|
|
94
|
+
return /^on[A-Z-]/.test(name);
|
|
95
|
+
}
|
|
96
|
+
function getEventHandlerName(name) {
|
|
97
|
+
return name[2] === "-" ? name.slice(3) : name.slice(2).toLowerCase();
|
|
98
|
+
}
|
|
99
|
+
function isNotVoid(value) {
|
|
100
|
+
return value != null && value !== false;
|
|
101
|
+
}
|
|
102
|
+
function normalizeDynamicRenderer(value) {
|
|
103
|
+
if (value) {
|
|
104
|
+
if (typeof value === "string") return value;
|
|
105
|
+
const normalized = value.content || value.default || value;
|
|
106
|
+
if ("id" in normalized) return normalized;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
22
110
|
//#region src/common/errors.ts
|
|
111
|
+
const lowercaseEventHandlerReg = /^on[a-z]/;
|
|
112
|
+
function assertValidAttrValue(name, value) {
|
|
113
|
+
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.`);
|
|
114
|
+
if (typeof value === "function") {
|
|
115
|
+
if (name === "content" || /^on/i.test(name) || /Change$/.test(name)) return;
|
|
116
|
+
throw new Error(`The \`${name}\` attribute cannot be a function.`);
|
|
117
|
+
}
|
|
118
|
+
const unrenderable = describeUnrenderable(value);
|
|
119
|
+
if (unrenderable) throw new Error(`The \`${name}\` attribute cannot be ${unrenderable}.`);
|
|
120
|
+
}
|
|
121
|
+
function assertValidTextValue(value) {
|
|
122
|
+
const unrenderable = describeUnrenderable(value);
|
|
123
|
+
if (unrenderable) throw new Error(`Text content cannot be ${unrenderable}.`);
|
|
124
|
+
}
|
|
125
|
+
function describeUnrenderable(value) {
|
|
126
|
+
if (typeof value === "symbol") return "a symbol";
|
|
127
|
+
if (typeof value === "object" && value !== null) {
|
|
128
|
+
let stringified;
|
|
129
|
+
try {
|
|
130
|
+
stringified = `${value}`;
|
|
131
|
+
} catch {
|
|
132
|
+
stringified = "[object Object]";
|
|
133
|
+
}
|
|
134
|
+
if (/^\[object \w+\]$/.test(stringified)) return stringified === "[object Promise]" ? "a promise (use the `<await>` tag to render its resolved value)" : stringified === "[object Object]" ? "a plain object (it would render as `[object Object]`)" : `a value that renders as \`${stringified}\``;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function assertValidLoopKey(key, seenKeys) {
|
|
138
|
+
if (typeof key !== "string" && typeof key !== "number") throw new Error(`A \`<for>\` tag's \`by\` attribute must return a string or number for each item, but received ${key === null ? "null" : `type "${typeof key}"`}.`);
|
|
139
|
+
if (seenKeys) {
|
|
140
|
+
if (seenKeys.has(key)) throw new Error(`A \`<for>\` tag's \`by\` attribute must return a unique value for each item, but \`${key}\` was used more than once.`);
|
|
141
|
+
seenKeys.add(key);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function assertValidAttrName(name) {
|
|
145
|
+
if (htmlAttrNameReg.test(name)) throw new Error(`Invalid attribute name: ${JSON.stringify(name)}`);
|
|
146
|
+
const suggestion = getWrongAttrSuggestion(name);
|
|
147
|
+
if (suggestion) throw new Error(`\`${name}\` is not a valid attribute, did you mean \`${suggestion}\`?`);
|
|
148
|
+
}
|
|
23
149
|
function _el_read_error() {
|
|
24
150
|
throw new Error("Element references can only be read in scripts and event handlers.");
|
|
25
151
|
}
|
|
@@ -44,6 +170,9 @@ function assertExclusiveAttrs(attrs, onError = throwErr) {
|
|
|
44
170
|
if (exclusiveAttrs && exclusiveAttrs.length > 1) onError(`The attributes ${joinWithAnd(exclusiveAttrs)} are mutually exclusive.`);
|
|
45
171
|
}
|
|
46
172
|
}
|
|
173
|
+
function assertHandlerIsFunction(name, value) {
|
|
174
|
+
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}".`);
|
|
175
|
+
}
|
|
47
176
|
function assertValidTagName(tagName) {
|
|
48
177
|
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
178
|
}
|
|
@@ -80,56 +209,6 @@ function forUntil(until, from, step, cb) {
|
|
|
80
209
|
for (let steps = (until - start) / delta, i = 0; i < steps; i++) cb(start + i * delta);
|
|
81
210
|
}
|
|
82
211
|
//#endregion
|
|
83
|
-
//#region src/common/helpers.ts
|
|
84
|
-
const htmlAttrNameReg = /^[^a-z_]|[^a-z0-9._:-]/i;
|
|
85
|
-
function _call(fn, v) {
|
|
86
|
-
fn(v);
|
|
87
|
-
return v;
|
|
88
|
-
}
|
|
89
|
-
function stringifyClassObject(name, value) {
|
|
90
|
-
return value ? name : "";
|
|
91
|
-
}
|
|
92
|
-
function stringifyStyleObject(name, value) {
|
|
93
|
-
return value || value === 0 ? name + ":" + value : "";
|
|
94
|
-
}
|
|
95
|
-
const toDelimitedString = function toDelimitedString(val, delimiter, stringify) {
|
|
96
|
-
let str = "";
|
|
97
|
-
let sep = "";
|
|
98
|
-
let part;
|
|
99
|
-
if (val) if (typeof val !== "object") str += val;
|
|
100
|
-
else if (Array.isArray(val)) for (const v of val) {
|
|
101
|
-
part = toDelimitedString(v, delimiter, stringify);
|
|
102
|
-
if (part) {
|
|
103
|
-
str += sep + part;
|
|
104
|
-
sep = delimiter;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
else for (const name in val) {
|
|
108
|
-
part = stringify(name, val[name]);
|
|
109
|
-
if (part) {
|
|
110
|
-
str += sep + part;
|
|
111
|
-
sep = delimiter;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return str;
|
|
115
|
-
};
|
|
116
|
-
function isEventHandler(name) {
|
|
117
|
-
return /^on[A-Z-]/.test(name);
|
|
118
|
-
}
|
|
119
|
-
function getEventHandlerName(name) {
|
|
120
|
-
return name[2] === "-" ? name.slice(3) : name.slice(2).toLowerCase();
|
|
121
|
-
}
|
|
122
|
-
function isNotVoid(value) {
|
|
123
|
-
return value != null && value !== false;
|
|
124
|
-
}
|
|
125
|
-
function normalizeDynamicRenderer(value) {
|
|
126
|
-
if (value) {
|
|
127
|
-
if (typeof value === "string") return value;
|
|
128
|
-
const normalized = value.content || value.default || value;
|
|
129
|
-
if ("id" in normalized) return normalized;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
//#endregion
|
|
133
212
|
//#region src/common/meta.ts
|
|
134
213
|
const DYNAMIC_TAG_SCRIPT_REGISTER_ID = "_dynamicTagScript";
|
|
135
214
|
//#endregion
|
|
@@ -144,6 +223,7 @@ function push(opt, item) {
|
|
|
144
223
|
//#region src/dom/event.ts
|
|
145
224
|
const defaultDelegator = /* @__PURE__ */ createDelegator();
|
|
146
225
|
function _on(element, type, handler) {
|
|
226
|
+
assertHandlerIsFunction("on" + type[0].toUpperCase() + type.slice(1), handler);
|
|
147
227
|
if (element["$" + type] === void 0) defaultDelegator(element, type, handleDelegated);
|
|
148
228
|
element["$" + type] = handler || null;
|
|
149
229
|
}
|
|
@@ -791,6 +871,7 @@ function _attr_input_checked_default(scope, nodeAccessor, checked) {
|
|
|
791
871
|
function _attr_input_checked(scope, nodeAccessor, checked, checkedChange) {
|
|
792
872
|
const el = scope[nodeAccessor];
|
|
793
873
|
const normalizedChecked = isNotVoid(checked);
|
|
874
|
+
assertHandlerIsFunction("checkedChange", checkedChange);
|
|
794
875
|
scope["ControlledHandler:" + nodeAccessor] = checkedChange;
|
|
795
876
|
scope["ControlledType:" + nodeAccessor] = checkedChange ? 0 : 5;
|
|
796
877
|
if (checkedChange && scope["#Gen"] < runId) el.checked = normalizedChecked;
|
|
@@ -819,6 +900,7 @@ function _attr_input_checkedValue(scope, nodeAccessor, checkedValue, checkedValu
|
|
|
819
900
|
const el = scope[nodeAccessor];
|
|
820
901
|
const multiple = Array.isArray(checkedValue);
|
|
821
902
|
const normalizedCheckedValue = scope["ControlledValue:" + nodeAccessor] = multiple ? checkedValue.map(normalizeStrProp) : normalizeStrProp(checkedValue);
|
|
903
|
+
assertHandlerIsFunction("checkedValueChange", checkedValueChange);
|
|
822
904
|
scope["ControlledHandler:" + nodeAccessor] = checkedValueChange;
|
|
823
905
|
scope["ControlledType:" + nodeAccessor] = checkedValueChange ? 1 : 5;
|
|
824
906
|
if (checkedValueChange && scope["#Gen"] < runId) {
|
|
@@ -855,6 +937,7 @@ function _attr_input_value_default(scope, nodeAccessor, value) {
|
|
|
855
937
|
function _attr_input_value(scope, nodeAccessor, value, valueChange) {
|
|
856
938
|
const el = scope[nodeAccessor];
|
|
857
939
|
const normalizedValue = normalizeAttrValue(value) || "";
|
|
940
|
+
assertHandlerIsFunction("valueChange", valueChange);
|
|
858
941
|
scope["ControlledHandler:" + nodeAccessor] = valueChange;
|
|
859
942
|
scope["ControlledValue:" + nodeAccessor] = normalizedValue;
|
|
860
943
|
scope["ControlledType:" + nodeAccessor] = valueChange ? 2 : 5;
|
|
@@ -903,8 +986,10 @@ function _attr_select_value(scope, nodeAccessor, value, valueChange) {
|
|
|
903
986
|
const existing = scope["#Gen"] < runId;
|
|
904
987
|
const multiple = Array.isArray(value);
|
|
905
988
|
const normalizedValue = scope["ControlledValue:" + nodeAccessor] = multiple ? value.map(normalizeStrProp) : normalizeStrProp(value);
|
|
989
|
+
assertHandlerIsFunction("valueChange", valueChange);
|
|
906
990
|
scope["ControlledHandler:" + nodeAccessor] = valueChange;
|
|
907
991
|
scope["ControlledType:" + nodeAccessor] = valueChange ? 3 : 5;
|
|
992
|
+
if (valueChange) pendingEffects.unshift(() => assertSelectValueMatchesOption(el, normalizedValue, value), scope);
|
|
908
993
|
if (valueChange && existing) pendingEffects.unshift(() => setSelectValue(el, normalizedValue, multiple), scope);
|
|
909
994
|
else _attr_select_value_default(scope, nodeAccessor, normalizedValue);
|
|
910
995
|
}
|
|
@@ -947,11 +1032,19 @@ function setSelectValue(el, value, multiple) {
|
|
|
947
1032
|
function getSelectValue(el, multiple) {
|
|
948
1033
|
return multiple ? Array.from(el.selectedOptions, (opt) => opt.value) : el.value;
|
|
949
1034
|
}
|
|
1035
|
+
function assertSelectValueMatchesOption(el, normalizedValue, value) {
|
|
1036
|
+
const multiple = Array.isArray(normalizedValue);
|
|
1037
|
+
if (multiple ? normalizedValue.some(Boolean) : normalizedValue) {
|
|
1038
|
+
for (const opt of el.options) if (multiple ? normalizedValue.includes(opt.value) : opt.value === normalizedValue) return;
|
|
1039
|
+
console.error("A controlled `<select>`'s `value` has no matching `<option>`:", value);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
950
1042
|
function _attr_details_or_dialog_open_default(scope, nodeAccessor, open) {
|
|
951
1043
|
if (scope["#Gen"] === runId) scope[nodeAccessor].open = isNotVoid(open);
|
|
952
1044
|
}
|
|
953
1045
|
function _attr_details_or_dialog_open(scope, nodeAccessor, open, openChange) {
|
|
954
1046
|
const normalizedOpen = scope["ControlledValue:" + nodeAccessor] = isNotVoid(open);
|
|
1047
|
+
assertHandlerIsFunction("openChange", openChange);
|
|
955
1048
|
scope["ControlledHandler:" + nodeAccessor] = openChange;
|
|
956
1049
|
scope["ControlledType:" + nodeAccessor] = openChange ? 4 : 5;
|
|
957
1050
|
if (openChange && scope["#Gen"] < runId) scope[nodeAccessor].open = normalizedOpen;
|
|
@@ -1010,9 +1103,11 @@ function updateList(arr, val, push) {
|
|
|
1010
1103
|
//#endregion
|
|
1011
1104
|
//#region src/dom/dom.ts
|
|
1012
1105
|
function _to_text(value) {
|
|
1106
|
+
assertValidTextValue(value);
|
|
1013
1107
|
return value || value === 0 ? value + "" : "";
|
|
1014
1108
|
}
|
|
1015
1109
|
function _attr(element, name, value) {
|
|
1110
|
+
assertValidAttrValue(name, value);
|
|
1016
1111
|
setAttribute(element, name, normalizeAttrValue(value));
|
|
1017
1112
|
}
|
|
1018
1113
|
function setAttribute(element, name, value) {
|
|
@@ -1087,8 +1182,11 @@ function _attrs_partial_content(scope, nodeAccessor, nextAttrs, skip) {
|
|
|
1087
1182
|
}
|
|
1088
1183
|
function attrsInternal(scope, nodeAccessor, nextAttrs) {
|
|
1089
1184
|
const el = scope[nodeAccessor];
|
|
1090
|
-
let events;
|
|
1185
|
+
let events = scope["EventAttributes:" + nodeAccessor];
|
|
1091
1186
|
let skip;
|
|
1187
|
+
for (const name in events) events[name] = 0;
|
|
1188
|
+
scope["ControlledType:" + nodeAccessor] = 5;
|
|
1189
|
+
scope["ControlledHandler:" + nodeAccessor] = 0;
|
|
1092
1190
|
switch (el.tagName) {
|
|
1093
1191
|
case "INPUT":
|
|
1094
1192
|
if ("checked" in nextAttrs || "checkedChange" in nextAttrs) _attr_input_checked(scope, nodeAccessor, nextAttrs.checked, nextAttrs.checkedChange);
|
|
@@ -1127,7 +1225,7 @@ function attrsInternal(scope, nodeAccessor, nextAttrs) {
|
|
|
1127
1225
|
_attr_style(el, value);
|
|
1128
1226
|
break;
|
|
1129
1227
|
default:
|
|
1130
|
-
|
|
1228
|
+
assertValidAttrName(name);
|
|
1131
1229
|
if (isEventHandler(name)) (events ||= scope["EventAttributes:" + nodeAccessor] = {})[getEventHandlerName(name)] = value;
|
|
1132
1230
|
else if (!(skip?.test(name) || name === "content" && el.tagName !== "META")) _attr(el, name, value);
|
|
1133
1231
|
break;
|
|
@@ -1372,7 +1470,7 @@ function _if(nodeAccessor, ...branchesArgs) {
|
|
|
1372
1470
|
while (i < branchesArgs.length) branches.push(_content("", branchesArgs[i++], branchesArgs[i++], branchesArgs[i++])());
|
|
1373
1471
|
enableBranches();
|
|
1374
1472
|
return (scope, newBranch) => {
|
|
1375
|
-
if (newBranch !== scope[branchAccessor]) setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
|
|
1473
|
+
if (newBranch !== (scope[branchAccessor] ?? (scope["BranchScopes:" + nodeAccessor] && 0))) setConditionalRenderer(scope, nodeAccessor, branches[scope[branchAccessor] = newBranch], createAndSetupBranch);
|
|
1376
1474
|
};
|
|
1377
1475
|
}
|
|
1378
1476
|
function patchDynamicTag(fn) {
|
|
@@ -1464,8 +1562,7 @@ function loop(forEach) {
|
|
|
1464
1562
|
let hasPotentialMoves;
|
|
1465
1563
|
var seenKeys = /* @__PURE__ */ new Set();
|
|
1466
1564
|
forEach(value, (key, args) => {
|
|
1467
|
-
|
|
1468
|
-
else seenKeys.add(key);
|
|
1565
|
+
assertValidLoopKey(key, seenKeys);
|
|
1469
1566
|
let branch = oldLen && (oldScopesByKey ||= oldScopes.reduce((map, scope, i) => map.set(scope["#LoopKey"] ?? i, scope), /* @__PURE__ */ new Map())).get(key);
|
|
1470
1567
|
if (branch) hasPotentialMoves = oldScopesByKey.delete(key);
|
|
1471
1568
|
else branch = createAndSetupBranch(scope["$global"], renderer, scope, parentNode);
|