marko 6.1.13 → 6.1.15

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.
@@ -21,8 +21,136 @@ 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 stringifyClassObject(name, value) {
65
+ return value ? name : "";
66
+ }
67
+ function stringifyStyleObject(name, value) {
68
+ return value || value === 0 ? name + ":" + value : "";
69
+ }
70
+ const toDelimitedString = function toDelimitedString(val, delimiter, stringify) {
71
+ let str = "";
72
+ let sep = "";
73
+ let part;
74
+ if (val) if (typeof val !== "object") str += val;
75
+ else if (Array.isArray(val)) for (const v of val) {
76
+ part = toDelimitedString(v, delimiter, stringify);
77
+ if (part) {
78
+ str += sep + part;
79
+ sep = delimiter;
80
+ }
81
+ }
82
+ else for (const name in val) {
83
+ part = stringify(name, val[name]);
84
+ if (part) {
85
+ str += sep + part;
86
+ sep = delimiter;
87
+ }
88
+ }
89
+ return str;
90
+ };
91
+ function isEventHandler(name) {
92
+ return /^on[A-Z-]/.test(name);
93
+ }
94
+ function getEventHandlerName(name) {
95
+ return name[2] === "-" ? name.slice(3) : name.slice(2).toLowerCase();
96
+ }
97
+ function isVoid(value) {
98
+ return value == null || value === false;
99
+ }
100
+ function isNotVoid(value) {
101
+ return value != null && value !== false;
102
+ }
103
+ function normalizeDynamicRenderer(value) {
104
+ if (value) {
105
+ if (typeof value === "string") return value;
106
+ const normalized = value.content || value.default || value;
107
+ if (!((typeof normalized === "object" || typeof normalized === "function") && "id" in normalized)) {
108
+ if (value.content) throw new Error(`A dynamic tag must be a string tag name (like \`"div"\`) or a Marko template/component, but received an object whose \`content\` is not a template/component.`);
109
+ if (typeof value !== "object" && typeof value !== "function") throw new Error(`A dynamic tag must be a string tag name (like \`"div"\`) or a Marko template/component, but received a ${typeof value}.`);
110
+ }
111
+ if ("id" in normalized) return normalized;
112
+ }
113
+ }
114
+ //#endregion
24
115
  //#region src/common/errors.ts
25
116
  const lowercaseEventHandlerReg = /^on[a-z]/;
117
+ function assertValidAttrValue(name, value) {
118
+ 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.`);
119
+ if (typeof value === "function") {
120
+ if (name === "content" || /^on/i.test(name) || /Change$/.test(name)) return;
121
+ throw new Error(`The \`${name}\` attribute cannot be a function.`);
122
+ }
123
+ const unrenderable = describeUnrenderable(value);
124
+ if (unrenderable) throw new Error(`The \`${name}\` attribute cannot be ${unrenderable}.`);
125
+ }
126
+ function assertValidTextValue(value) {
127
+ const unrenderable = describeUnrenderable(value);
128
+ if (unrenderable) throw new Error(`Text content cannot be ${unrenderable}.`);
129
+ }
130
+ function describeUnrenderable(value) {
131
+ if (typeof value === "symbol") return "a symbol";
132
+ if (typeof value === "object" && value !== null) {
133
+ let stringified;
134
+ try {
135
+ stringified = `${value}`;
136
+ } catch {
137
+ stringified = "[object Object]";
138
+ }
139
+ 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}\``;
140
+ }
141
+ }
142
+ function assertValidLoopKey(key, seenKeys) {
143
+ 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}"`}.`);
144
+ if (seenKeys) {
145
+ 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.`);
146
+ seenKeys.add(key);
147
+ }
148
+ }
149
+ function assertValidAttrName(name) {
150
+ if (htmlAttrNameReg.test(name)) throw new Error(`Invalid attribute name: ${JSON.stringify(name)}`);
151
+ const suggestion = getWrongAttrSuggestion(name);
152
+ if (suggestion) throw new Error(`\`${name}\` is not a valid attribute, did you mean \`${suggestion}\`?`);
153
+ }
26
154
  function _el_read_error() {
27
155
  throw new Error("Element references can only be read in scripts and event handlers.");
28
156
  }
@@ -47,9 +175,6 @@ function assertExclusiveAttrs(attrs, onError = throwErr) {
47
175
  if (exclusiveAttrs && exclusiveAttrs.length > 1) onError(`The attributes ${joinWithAnd(exclusiveAttrs)} are mutually exclusive.`);
48
176
  }
49
177
  }
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
178
  function assertHandlerIsFunction(name, value) {
54
179
  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
180
  }
@@ -73,27 +198,32 @@ const DYNAMIC_TAG_SCRIPT_REGISTER_ID = "_dynamicTagScript";
73
198
  //#endregion
74
199
  //#region src/html/content.ts
75
200
  function _unescaped(val) {
201
+ assertValidTextValue(val);
76
202
  return val ? val + "" : val === 0 ? "0" : "";
77
203
  }
78
204
  const unsafeXMLReg = /[<&]/g;
79
205
  const replaceUnsafeXML = (c) => c === "&" ? "&amp;" : "&lt;";
80
206
  const escapeXMLStr = (str) => unsafeXMLReg.test(str) ? str.replace(unsafeXMLReg, replaceUnsafeXML) : str;
81
207
  function _escape(val) {
208
+ assertValidTextValue(val);
82
209
  return val ? escapeXMLStr(val + "") : val === 0 ? "0" : "";
83
210
  }
84
211
  const unsafeScriptReg = /<\/script/gi;
85
212
  const escapeScriptStr = (str) => unsafeScriptReg.test(str) ? str.replace(unsafeScriptReg, "\\x3C/script") : str;
86
213
  function _escape_script(val) {
214
+ assertValidTextValue(val);
87
215
  return val ? escapeScriptStr(val + "") : val === 0 ? "0" : "";
88
216
  }
89
217
  const unsafeStyleReg = /<\/style/gi;
90
218
  const escapeStyleStr = (str) => unsafeStyleReg.test(str) ? str.replace(unsafeStyleReg, "\\3C/style") : str;
91
219
  function _escape_style(val) {
220
+ assertValidTextValue(val);
92
221
  return val ? escapeStyleStr(val + "") : val === 0 ? "0" : "";
93
222
  }
94
223
  const unsafeCommentReg = />/g;
95
224
  const escapeCommentStr = (str) => unsafeCommentReg.test(str) ? str.replace(unsafeCommentReg, "&gt;") : str;
96
225
  function _escape_comment(val) {
226
+ assertValidTextValue(val);
97
227
  return val ? escapeCommentStr(val + "") : val === 0 ? "0" : "";
98
228
  }
99
229
  //#endregion
@@ -1159,6 +1289,7 @@ function isCircular(parent, ref) {
1159
1289
  }
1160
1290
  function toObjectKey(name) {
1161
1291
  if (name === "") return "\"\"";
1292
+ if (name === "__proto__") return "[\"__proto__\"]";
1162
1293
  const startChar = name[0];
1163
1294
  if (isDigit(startChar)) {
1164
1295
  if (startChar === "0") {
@@ -1171,7 +1302,7 @@ function toObjectKey(name) {
1171
1302
  }
1172
1303
  function toAccess(accessor) {
1173
1304
  const start = accessor[0];
1174
- return start === "\"" || start >= "0" && start <= "9" ? "[" + accessor + "]" : "." + accessor;
1305
+ return start === "[" ? accessor : start === "\"" || start >= "0" && start <= "9" ? "[" + accessor + "]" : "." + accessor;
1175
1306
  }
1176
1307
  function quote(str, startPos) {
1177
1308
  let result = "";
@@ -1313,55 +1444,6 @@ function patchIteratorNext(proto) {
1313
1444
  proto.next[kTouchedIterator] = true;
1314
1445
  }
1315
1446
  //#endregion
1316
- //#region src/common/helpers.ts
1317
- const htmlAttrNameReg = /^[^a-z_]|[^a-z0-9._:-]/i;
1318
- function stringifyClassObject(name, value) {
1319
- return value ? name : "";
1320
- }
1321
- function stringifyStyleObject(name, value) {
1322
- return value || value === 0 ? name + ":" + value : "";
1323
- }
1324
- const toDelimitedString = function toDelimitedString(val, delimiter, stringify) {
1325
- let str = "";
1326
- let sep = "";
1327
- let part;
1328
- if (val) if (typeof val !== "object") str += val;
1329
- else if (Array.isArray(val)) for (const v of val) {
1330
- part = toDelimitedString(v, delimiter, stringify);
1331
- if (part) {
1332
- str += sep + part;
1333
- sep = delimiter;
1334
- }
1335
- }
1336
- else for (const name in val) {
1337
- part = stringify(name, val[name]);
1338
- if (part) {
1339
- str += sep + part;
1340
- sep = delimiter;
1341
- }
1342
- }
1343
- return str;
1344
- };
1345
- function isEventHandler(name) {
1346
- return /^on[A-Z-]/.test(name);
1347
- }
1348
- function getEventHandlerName(name) {
1349
- return name[2] === "-" ? name.slice(3) : name.slice(2).toLowerCase();
1350
- }
1351
- function isVoid(value) {
1352
- return value == null || value === false;
1353
- }
1354
- function isNotVoid(value) {
1355
- return value != null && value !== false;
1356
- }
1357
- function normalizeDynamicRenderer(value) {
1358
- if (value) {
1359
- if (typeof value === "string") return value;
1360
- const normalized = value.content || value.default || value;
1361
- if ("id" in normalized) return normalized;
1362
- }
1363
- }
1364
- //#endregion
1365
1447
  //#region src/common/for.ts
1366
1448
  function forIn(obj, cb) {
1367
1449
  for (const key in obj) cb(key, obj[key]);
@@ -1404,28 +1486,13 @@ function concat(opt, other) {
1404
1486
  //#endregion
1405
1487
  //#region src/html/for.ts
1406
1488
  function forOfBy(by, item, index) {
1407
- if (by) {
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;
1411
- }
1412
- return index;
1489
+ return by ? typeof by === "string" ? item[by] : by(item, index) : index;
1413
1490
  }
1414
1491
  function forInBy(by, name, value) {
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
- }
1420
- return name;
1492
+ return by ? by(name, value) : name;
1421
1493
  }
1422
1494
  function forStepBy(by, index) {
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
- }
1428
- return index;
1495
+ return by ? by(index) : index;
1429
1496
  }
1430
1497
  //#endregion
1431
1498
  //#region src/html/inlined-runtimes.debug.ts
@@ -1686,20 +1753,31 @@ function _for_in(obj, cb, by, scopeId, accessor, serializeBranch, serializeMarke
1686
1753
  }) : forIn(obj, cb), scopeId, accessor, serializeBranch, serializeMarker, serializeStateful, parentEndTag, singleNode);
1687
1754
  }
1688
1755
  function _for_to(to, from, step, cb, by, scopeId, accessor, serializeBranch, serializeMarker, serializeStateful, parentEndTag, singleNode) {
1689
- forBranches(by, (each) => each ? forTo(to, from, step, (i) => {
1690
- const itemKey = forStepBy(by, i);
1691
- each(itemKey, itemKey === i, () => cb(i));
1692
- }) : forTo(to, from, step, cb), scopeId, accessor, serializeBranch, serializeMarker, serializeStateful, parentEndTag, singleNode);
1756
+ forBranches(by, (each) => {
1757
+ let index = 0;
1758
+ return each ? forTo(to, from, step, (value) => {
1759
+ const itemKey = forStepBy(by, value);
1760
+ each(itemKey, itemKey === index++, () => cb(value));
1761
+ }) : forTo(to, from, step, cb);
1762
+ }, scopeId, accessor, serializeBranch, serializeMarker, serializeStateful, parentEndTag, singleNode);
1693
1763
  }
1694
1764
  function _for_until(to, from, step, cb, by, scopeId, accessor, serializeBranch, serializeMarker, serializeStateful, parentEndTag, singleNode) {
1695
- forBranches(by, (each) => each ? forUntil(to, from, step, (i) => {
1696
- const itemKey = forStepBy(by, i);
1697
- each(itemKey, itemKey === i, () => cb(i));
1698
- }) : forUntil(to, from, step, cb), scopeId, accessor, serializeBranch, serializeMarker, serializeStateful, parentEndTag, singleNode);
1765
+ forBranches(by, (each) => {
1766
+ let index = 0;
1767
+ return each ? forUntil(to, from, step, (value) => {
1768
+ const itemKey = forStepBy(by, value);
1769
+ each(itemKey, itemKey === index++, () => cb(value));
1770
+ }) : forUntil(to, from, step, cb);
1771
+ }, scopeId, accessor, serializeBranch, serializeMarker, serializeStateful, parentEndTag, singleNode);
1699
1772
  }
1700
1773
  function forBranches(by, iterate, scopeId, accessor, serializeBranch, serializeMarker, serializeStateful, parentEndTag, singleNode) {
1774
+ var seenKeys = /* @__PURE__ */ new Set();
1701
1775
  if (serializeBranch === 0) {
1702
- iterate(0);
1776
+ if (by) iterate((itemKey, _sameAsIndex, render) => {
1777
+ assertValidLoopKey(itemKey, seenKeys);
1778
+ render();
1779
+ });
1780
+ else iterate(0);
1703
1781
  writeBranchEnd(scopeId, accessor, serializeStateful, serializeMarker, parentEndTag, singleNode, "");
1704
1782
  return;
1705
1783
  }
@@ -1708,13 +1786,9 @@ function forBranches(by, iterate, scopeId, accessor, serializeBranch, serializeM
1708
1786
  const resumeMarker = serializeMarker !== 0 && (!parentEndTag || serializeStateful !== 0);
1709
1787
  let flushBranchIds = "";
1710
1788
  let loopScopes;
1711
- var seenKeys = /* @__PURE__ */ new Set();
1712
1789
  iterate((itemKey, sameAsIndex, render) => {
1713
1790
  const branchId = _peek_scope_id();
1714
- if (by) {
1715
- if (seenKeys.has(itemKey)) console.error(`A <for> tag's \`by\` attribute must return a unique value for each item, but a duplicate was found matching:`, itemKey);
1716
- seenKeys.add(itemKey);
1717
- }
1791
+ if (by) assertValidLoopKey(itemKey, seenKeys);
1718
1792
  if (resumeMarker) if (singleNode) flushBranchIds = " " + branchId + flushBranchIds;
1719
1793
  else {
1720
1794
  $chunk.writeHTML(state.mark("[", flushBranchIds));
@@ -2386,12 +2460,24 @@ function _attr_style(value) {
2386
2460
  }
2387
2461
  function _attr_option_value(value) {
2388
2462
  const valueAttr = _attr("value", value);
2389
- return normalizedValueMatches(getContext(kSelectedValue), value) ? valueAttr + " selected" : valueAttr;
2463
+ if (normalizedValueMatches(getContext(kSelectedValue), value)) {
2464
+ {
2465
+ const matched = getContext(kSelectedValueMatched);
2466
+ if (matched) matched.value = true;
2467
+ }
2468
+ return valueAttr + " selected";
2469
+ }
2470
+ return valueAttr;
2390
2471
  }
2391
2472
  const kSelectedValue = Symbol("selectedValue");
2473
+ const kSelectedValueMatched = Symbol("selectedValueMatched");
2392
2474
  function _attr_select_value(scopeId, nodeAccessor, value, valueChange, content) {
2393
2475
  if (valueChange) writeControlledScope(3, scopeId, nodeAccessor, void 0, valueChange);
2394
- if (content) withContext(kSelectedValue, value, content);
2476
+ if (content) if (valueChange) {
2477
+ const matched = { value: false };
2478
+ withContext(kSelectedValue, value, () => withContext(kSelectedValueMatched, matched, content));
2479
+ if (!matched.value && (Array.isArray(value) ? value.some((v) => normalizeStrAttrValue(v) !== "") : normalizeStrAttrValue(value) !== "")) console.error("A controlled `<select>`'s `value` has no matching `<option>`:", value);
2480
+ } else withContext(kSelectedValue, value, content);
2395
2481
  }
2396
2482
  function _attr_textarea_value(scopeId, nodeAccessor, value, valueChange) {
2397
2483
  if (valueChange) writeControlledScope(2, scopeId, nodeAccessor, void 0, valueChange);
@@ -2430,7 +2516,6 @@ function _attr_nonce() {
2430
2516
  return getChunk().boundary.state.nonceAttr;
2431
2517
  }
2432
2518
  function _attr(name, value) {
2433
- assertValidEventHandlerAttr(name, value);
2434
2519
  return isVoid(value) ? "" : nonVoidAttr(name, value);
2435
2520
  }
2436
2521
  function _attrs(data, nodeAccessor, scopeId, tagName) {
@@ -2475,7 +2560,7 @@ function _attrs(data, nodeAccessor, scopeId, tagName) {
2475
2560
  break;
2476
2561
  default:
2477
2562
  if (name && !(isVoid(value) || skip.test(name) || name === "content" && tagName !== "meta")) {
2478
- if (htmlAttrNameReg.test(name)) throw new Error(`Invalid attribute name: ${JSON.stringify(name)}`);
2563
+ assertValidAttrName(name);
2479
2564
  if (isEventHandler(name)) {
2480
2565
  if (!events) {
2481
2566
  events = {};
@@ -2517,6 +2602,7 @@ function stringAttr(name, value) {
2517
2602
  return value && " " + name + attrAssignment(value);
2518
2603
  }
2519
2604
  function nonVoidAttr(name, value) {
2605
+ assertValidAttrValue(name, value);
2520
2606
  switch (typeof value) {
2521
2607
  case "string": return " " + name + attrAssignment(value);
2522
2608
  case "boolean": return " " + name;
@@ -2561,7 +2647,6 @@ const voidElementsReg = /^(?:area|b(?:ase|r)|col|embed|hr|i(?:mg|nput)|link|meta
2561
2647
  let _dynamic_tag = (scopeId, accessor, tag, inputOrArgs, content, inputIsArgs, serializeReason) => {
2562
2648
  const shouldResume = serializeReason !== 0;
2563
2649
  const renderer = normalizeDynamicRenderer(tag);
2564
- if (renderer && typeof renderer !== "function" && typeof renderer !== "string") throw new Error(`Invalid renderer passed for dynamic tag: ${renderer}`);
2565
2650
  const state = getState();
2566
2651
  const branchId = _peek_scope_id();
2567
2652
  let rendered;