@lightspeed-js/core 0.4.4 → 0.4.6
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/compile.js +81 -3
- package/dist/index.js +106 -10
- package/dist/ssg.js +114 -11
- package/dist/types/src/runtime/dom/ForBlock.d.ts +18 -1
- package/dist/types/test/tag.test.d.ts +1 -0
- package/dist/vite.js +114 -11
- package/package.json +1 -1
package/dist/compile.js
CHANGED
|
@@ -999,7 +999,7 @@ class BindingEvaluator {
|
|
|
999
999
|
}
|
|
1000
1000
|
if (binding.kind === "prop") {
|
|
1001
1001
|
const target = binding.target;
|
|
1002
|
-
if (
|
|
1002
|
+
if (sameValue(target.props[binding.name], value))
|
|
1003
1003
|
return;
|
|
1004
1004
|
target.props[binding.name] = value;
|
|
1005
1005
|
this.onPropChanged(target);
|
|
@@ -1112,7 +1112,51 @@ function setHidden(el, hidden) {
|
|
|
1112
1112
|
else
|
|
1113
1113
|
el.removeAttribute(HIDDEN_ATTR);
|
|
1114
1114
|
}
|
|
1115
|
+
function sameValue(a, b, depth = 0) {
|
|
1116
|
+
if (Object.is(a, b))
|
|
1117
|
+
return true;
|
|
1118
|
+
if (depth > 20 || !isPlain(a) || !isPlain(b))
|
|
1119
|
+
return false;
|
|
1120
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
1121
|
+
return false;
|
|
1122
|
+
const keys = Object.keys(a);
|
|
1123
|
+
if (keys.length !== Object.keys(b).length)
|
|
1124
|
+
return false;
|
|
1125
|
+
return keys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && sameValue(a[key], b[key], depth + 1));
|
|
1126
|
+
}
|
|
1127
|
+
function isPlain(value) {
|
|
1128
|
+
if (value === null || typeof value !== "object")
|
|
1129
|
+
return false;
|
|
1130
|
+
if (Array.isArray(value))
|
|
1131
|
+
return true;
|
|
1132
|
+
const proto = Object.getPrototypeOf(value);
|
|
1133
|
+
return proto === Object.prototype || proto === null;
|
|
1134
|
+
}
|
|
1115
1135
|
// src/runtime/dom/ForBlock.ts
|
|
1136
|
+
var TAG_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
1137
|
+
var REFUSED_TAGS = new Set([
|
|
1138
|
+
"script",
|
|
1139
|
+
"style",
|
|
1140
|
+
"template",
|
|
1141
|
+
"slot",
|
|
1142
|
+
"iframe",
|
|
1143
|
+
"object",
|
|
1144
|
+
"embed"
|
|
1145
|
+
]);
|
|
1146
|
+
function retag(source, name) {
|
|
1147
|
+
const doc = source.ownerDocument;
|
|
1148
|
+
const el = source.namespaceURI ? doc.createElementNS(source.namespaceURI, name) : doc.createElement(name);
|
|
1149
|
+
for (const attr of Array.from(source.attributes)) {
|
|
1150
|
+
if (attr.namespaceURI)
|
|
1151
|
+
el.setAttributeNS(attr.namespaceURI, attr.name, attr.value);
|
|
1152
|
+
else
|
|
1153
|
+
el.setAttribute(attr.name, attr.value);
|
|
1154
|
+
}
|
|
1155
|
+
for (const child of Array.from(source.childNodes)) {
|
|
1156
|
+
el.appendChild(child.cloneNode(true));
|
|
1157
|
+
}
|
|
1158
|
+
return el;
|
|
1159
|
+
}
|
|
1116
1160
|
function toIterable(value) {
|
|
1117
1161
|
if (value == null)
|
|
1118
1162
|
return [];
|
|
@@ -1154,12 +1198,13 @@ class ForBlock {
|
|
|
1154
1198
|
renderer;
|
|
1155
1199
|
conditional;
|
|
1156
1200
|
itemsExpr;
|
|
1201
|
+
tagged;
|
|
1157
1202
|
rows = new Map;
|
|
1158
1203
|
order = [];
|
|
1159
1204
|
pendingItems = null;
|
|
1160
1205
|
running = false;
|
|
1161
1206
|
warnedDuplicates = false;
|
|
1162
|
-
constructor(owner, anchor, templates, alias, params, parentLocals, keyFor, renderer, conditional = false, itemsExpr = "") {
|
|
1207
|
+
constructor(owner, anchor, templates, alias, params, parentLocals, keyFor, renderer, conditional = false, itemsExpr = "", tagged = false) {
|
|
1163
1208
|
this.owner = owner;
|
|
1164
1209
|
this.anchor = anchor;
|
|
1165
1210
|
this.templates = templates;
|
|
@@ -1170,22 +1215,55 @@ class ForBlock {
|
|
|
1170
1215
|
this.renderer = renderer;
|
|
1171
1216
|
this.conditional = conditional;
|
|
1172
1217
|
this.itemsExpr = itemsExpr;
|
|
1218
|
+
this.tagged = tagged;
|
|
1173
1219
|
}
|
|
1220
|
+
retagged = new Map;
|
|
1221
|
+
warnedTag = false;
|
|
1174
1222
|
toItems(value) {
|
|
1175
1223
|
if (this.conditional) {
|
|
1176
1224
|
return Number.isInteger(value) && value >= 0 ? [value] : [];
|
|
1177
1225
|
}
|
|
1226
|
+
if (this.tagged)
|
|
1227
|
+
return [this.tagFor(value)];
|
|
1178
1228
|
return toIterable(value);
|
|
1179
1229
|
}
|
|
1180
1230
|
templateFor(item) {
|
|
1231
|
+
if (this.tagged) {
|
|
1232
|
+
const source = this.templates[0];
|
|
1233
|
+
if (item === source.localName)
|
|
1234
|
+
return source;
|
|
1235
|
+
let made = this.retagged.get(item);
|
|
1236
|
+
if (!made) {
|
|
1237
|
+
made = retag(source, item);
|
|
1238
|
+
this.retagged.set(item, made);
|
|
1239
|
+
}
|
|
1240
|
+
return made;
|
|
1241
|
+
}
|
|
1181
1242
|
return this.conditional ? this.templates[item] : this.templates[0];
|
|
1182
1243
|
}
|
|
1244
|
+
tagFor(value) {
|
|
1245
|
+
const written = this.templates[0].localName;
|
|
1246
|
+
if (value == null || value === "")
|
|
1247
|
+
return written;
|
|
1248
|
+
const name = typeof value === "string" ? value.trim().toLowerCase() : null;
|
|
1249
|
+
if (name && TAG_NAME.test(name) && !REFUSED_TAGS.has(name))
|
|
1250
|
+
return name;
|
|
1251
|
+
if (!this.warnedTag) {
|
|
1252
|
+
this.warnedTag = true;
|
|
1253
|
+
warn(`$tag="${this.itemsExpr}" gave ${JSON.stringify(value)}, which is not an element it can make - rendering <${written}>.`);
|
|
1254
|
+
}
|
|
1255
|
+
return written;
|
|
1256
|
+
}
|
|
1183
1257
|
schedule(items) {
|
|
1184
1258
|
this.pendingItems = items;
|
|
1185
1259
|
if (this.running)
|
|
1186
1260
|
return;
|
|
1187
1261
|
this.running = true;
|
|
1188
|
-
this.drain();
|
|
1262
|
+
this.draining = this.drain();
|
|
1263
|
+
}
|
|
1264
|
+
draining = null;
|
|
1265
|
+
settled() {
|
|
1266
|
+
return this.draining ?? Promise.resolve();
|
|
1189
1267
|
}
|
|
1190
1268
|
async drain() {
|
|
1191
1269
|
try {
|
package/dist/index.js
CHANGED
|
@@ -571,7 +571,7 @@ class BindingEvaluator {
|
|
|
571
571
|
}
|
|
572
572
|
if (binding.kind === "prop") {
|
|
573
573
|
const target = binding.target;
|
|
574
|
-
if (
|
|
574
|
+
if (sameValue(target.props[binding.name], value))
|
|
575
575
|
return;
|
|
576
576
|
target.props[binding.name] = value;
|
|
577
577
|
this.onPropChanged(target);
|
|
@@ -684,6 +684,26 @@ function setHidden(el, hidden) {
|
|
|
684
684
|
else
|
|
685
685
|
el.removeAttribute(HIDDEN_ATTR);
|
|
686
686
|
}
|
|
687
|
+
function sameValue(a, b, depth = 0) {
|
|
688
|
+
if (Object.is(a, b))
|
|
689
|
+
return true;
|
|
690
|
+
if (depth > 20 || !isPlain(a) || !isPlain(b))
|
|
691
|
+
return false;
|
|
692
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
693
|
+
return false;
|
|
694
|
+
const keys = Object.keys(a);
|
|
695
|
+
if (keys.length !== Object.keys(b).length)
|
|
696
|
+
return false;
|
|
697
|
+
return keys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && sameValue(a[key], b[key], depth + 1));
|
|
698
|
+
}
|
|
699
|
+
function isPlain(value) {
|
|
700
|
+
if (value === null || typeof value !== "object")
|
|
701
|
+
return false;
|
|
702
|
+
if (Array.isArray(value))
|
|
703
|
+
return true;
|
|
704
|
+
const proto = Object.getPrototypeOf(value);
|
|
705
|
+
return proto === Object.prototype || proto === null;
|
|
706
|
+
}
|
|
687
707
|
// src/runtime/events/EventBinder.ts
|
|
688
708
|
class EventBinder {
|
|
689
709
|
bind(el, eventType, handlerName, owner, cleanupTarget = owner) {
|
|
@@ -1020,6 +1040,30 @@ class Row {
|
|
|
1020
1040
|
this.locals[this.locals.length - 1] = value;
|
|
1021
1041
|
}
|
|
1022
1042
|
}
|
|
1043
|
+
var TAG_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
1044
|
+
var REFUSED_TAGS = new Set([
|
|
1045
|
+
"script",
|
|
1046
|
+
"style",
|
|
1047
|
+
"template",
|
|
1048
|
+
"slot",
|
|
1049
|
+
"iframe",
|
|
1050
|
+
"object",
|
|
1051
|
+
"embed"
|
|
1052
|
+
]);
|
|
1053
|
+
function retag(source, name) {
|
|
1054
|
+
const doc = source.ownerDocument;
|
|
1055
|
+
const el = source.namespaceURI ? doc.createElementNS(source.namespaceURI, name) : doc.createElement(name);
|
|
1056
|
+
for (const attr of Array.from(source.attributes)) {
|
|
1057
|
+
if (attr.namespaceURI)
|
|
1058
|
+
el.setAttributeNS(attr.namespaceURI, attr.name, attr.value);
|
|
1059
|
+
else
|
|
1060
|
+
el.setAttribute(attr.name, attr.value);
|
|
1061
|
+
}
|
|
1062
|
+
for (const child of Array.from(source.childNodes)) {
|
|
1063
|
+
el.appendChild(child.cloneNode(true));
|
|
1064
|
+
}
|
|
1065
|
+
return el;
|
|
1066
|
+
}
|
|
1023
1067
|
function toIterable(value) {
|
|
1024
1068
|
if (value == null)
|
|
1025
1069
|
return [];
|
|
@@ -1061,12 +1105,13 @@ class ForBlock {
|
|
|
1061
1105
|
renderer;
|
|
1062
1106
|
conditional;
|
|
1063
1107
|
itemsExpr;
|
|
1108
|
+
tagged;
|
|
1064
1109
|
rows = new Map;
|
|
1065
1110
|
order = [];
|
|
1066
1111
|
pendingItems = null;
|
|
1067
1112
|
running = false;
|
|
1068
1113
|
warnedDuplicates = false;
|
|
1069
|
-
constructor(owner, anchor, templates, alias, params, parentLocals, keyFor, renderer, conditional = false, itemsExpr = "") {
|
|
1114
|
+
constructor(owner, anchor, templates, alias, params, parentLocals, keyFor, renderer, conditional = false, itemsExpr = "", tagged = false) {
|
|
1070
1115
|
this.owner = owner;
|
|
1071
1116
|
this.anchor = anchor;
|
|
1072
1117
|
this.templates = templates;
|
|
@@ -1077,22 +1122,55 @@ class ForBlock {
|
|
|
1077
1122
|
this.renderer = renderer;
|
|
1078
1123
|
this.conditional = conditional;
|
|
1079
1124
|
this.itemsExpr = itemsExpr;
|
|
1125
|
+
this.tagged = tagged;
|
|
1080
1126
|
}
|
|
1127
|
+
retagged = new Map;
|
|
1128
|
+
warnedTag = false;
|
|
1081
1129
|
toItems(value) {
|
|
1082
1130
|
if (this.conditional) {
|
|
1083
1131
|
return Number.isInteger(value) && value >= 0 ? [value] : [];
|
|
1084
1132
|
}
|
|
1133
|
+
if (this.tagged)
|
|
1134
|
+
return [this.tagFor(value)];
|
|
1085
1135
|
return toIterable(value);
|
|
1086
1136
|
}
|
|
1087
1137
|
templateFor(item) {
|
|
1138
|
+
if (this.tagged) {
|
|
1139
|
+
const source = this.templates[0];
|
|
1140
|
+
if (item === source.localName)
|
|
1141
|
+
return source;
|
|
1142
|
+
let made = this.retagged.get(item);
|
|
1143
|
+
if (!made) {
|
|
1144
|
+
made = retag(source, item);
|
|
1145
|
+
this.retagged.set(item, made);
|
|
1146
|
+
}
|
|
1147
|
+
return made;
|
|
1148
|
+
}
|
|
1088
1149
|
return this.conditional ? this.templates[item] : this.templates[0];
|
|
1089
1150
|
}
|
|
1151
|
+
tagFor(value) {
|
|
1152
|
+
const written = this.templates[0].localName;
|
|
1153
|
+
if (value == null || value === "")
|
|
1154
|
+
return written;
|
|
1155
|
+
const name = typeof value === "string" ? value.trim().toLowerCase() : null;
|
|
1156
|
+
if (name && TAG_NAME.test(name) && !REFUSED_TAGS.has(name))
|
|
1157
|
+
return name;
|
|
1158
|
+
if (!this.warnedTag) {
|
|
1159
|
+
this.warnedTag = true;
|
|
1160
|
+
warn(`$tag="${this.itemsExpr}" gave ${JSON.stringify(value)}, which is not an element it can make - rendering <${written}>.`);
|
|
1161
|
+
}
|
|
1162
|
+
return written;
|
|
1163
|
+
}
|
|
1090
1164
|
schedule(items) {
|
|
1091
1165
|
this.pendingItems = items;
|
|
1092
1166
|
if (this.running)
|
|
1093
1167
|
return;
|
|
1094
1168
|
this.running = true;
|
|
1095
|
-
this.drain();
|
|
1169
|
+
this.draining = this.drain();
|
|
1170
|
+
}
|
|
1171
|
+
draining = null;
|
|
1172
|
+
settled() {
|
|
1173
|
+
return this.draining ?? Promise.resolve();
|
|
1096
1174
|
}
|
|
1097
1175
|
async drain() {
|
|
1098
1176
|
try {
|
|
@@ -1446,6 +1524,15 @@ class Mounter {
|
|
|
1446
1524
|
this.createBlock(el, context, "if");
|
|
1447
1525
|
return;
|
|
1448
1526
|
}
|
|
1527
|
+
if (el.hasAttribute("data-bind-tag")) {
|
|
1528
|
+
if (el.tagName === "TEMPLATE") {
|
|
1529
|
+
warn("$tag on a <template> means nothing - it renders its contents, not itself.", el);
|
|
1530
|
+
el.removeAttribute("data-bind-tag");
|
|
1531
|
+
} else {
|
|
1532
|
+
this.createBlock(el, context, "tag");
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1449
1536
|
if (el.hasAttribute("data-bind-else") || el.hasAttribute("data-bind-else-if")) {
|
|
1450
1537
|
warn("$else / $else-if must directly follow a $if or $else-if sibling", el);
|
|
1451
1538
|
el.remove();
|
|
@@ -1591,6 +1678,13 @@ class Mounter {
|
|
|
1591
1678
|
i += consumed.templates - 1;
|
|
1592
1679
|
continue;
|
|
1593
1680
|
}
|
|
1681
|
+
if (el.hasAttribute("data-bind-tag") && el.tagName !== "TEMPLATE") {
|
|
1682
|
+
const consumed = this.adoptBlock(el, found, d, context, "tag");
|
|
1683
|
+
if (!consumed)
|
|
1684
|
+
return false;
|
|
1685
|
+
d += consumed.dom;
|
|
1686
|
+
continue;
|
|
1687
|
+
}
|
|
1594
1688
|
if (el.tagName === "SLOT") {
|
|
1595
1689
|
if (!found[d] || !isSlotStart(found[d])) {
|
|
1596
1690
|
if (!this.adoptNodes(Array.from(el.childNodes), found.slice(d), context, markers)) {
|
|
@@ -1726,18 +1820,19 @@ class Mounter {
|
|
|
1726
1820
|
const rawExpression = el.getAttribute(`data-bind-${kind}`) ?? "";
|
|
1727
1821
|
const branches = conditional ? this.gatherBranches(el) : [];
|
|
1728
1822
|
const expression = conditional ? composeBranchExpression(branches) : rawExpression;
|
|
1729
|
-
const parsed = conditional ? { alias: null, items: expression } : parseForExpression(rawExpression);
|
|
1823
|
+
const parsed = conditional || kind === "tag" ? { alias: null, items: expression } : parseForExpression(rawExpression);
|
|
1730
1824
|
if (!parsed) {
|
|
1731
1825
|
warn(`Could not parse $for="${expression}" - expected "item in items"`);
|
|
1732
1826
|
el.remove();
|
|
1733
1827
|
return null;
|
|
1734
1828
|
}
|
|
1735
|
-
if (
|
|
1829
|
+
if (kind === "for" && el.hasAttribute("data-bind-if")) {
|
|
1736
1830
|
warn(`$for and $if on the same element are not supported (${rawExpression}). Wrap one in an element.`);
|
|
1737
1831
|
}
|
|
1738
|
-
const keyExpr =
|
|
1832
|
+
const keyExpr = kind === "for" ? el.getAttribute("data-bind-key") : null;
|
|
1739
1833
|
el.removeAttribute(`data-bind-${kind}`);
|
|
1740
|
-
|
|
1834
|
+
if (kind === "for")
|
|
1835
|
+
el.removeAttribute("data-bind-key");
|
|
1741
1836
|
let anchor;
|
|
1742
1837
|
if (existingAnchor) {
|
|
1743
1838
|
anchor = existingAnchor;
|
|
@@ -1752,7 +1847,7 @@ class Mounter {
|
|
|
1752
1847
|
const compiled = this.bindings.compileExpression(keyExpr, params);
|
|
1753
1848
|
return compiled(context.owner.state, context.owner.props, this.cellsProxy(), this.bindings.pendingProbe(), ...locals);
|
|
1754
1849
|
} : null;
|
|
1755
|
-
const block = new ForBlock(context.owner, anchor, templates, parsed.alias, context.params, context.locals, keyFor, this, conditional, parsed.items);
|
|
1850
|
+
const block = new ForBlock(context.owner, anchor, templates, parsed.alias, context.params, context.locals, keyFor, this, conditional, parsed.items, kind === "tag");
|
|
1756
1851
|
context.holder.addBlock(block);
|
|
1757
1852
|
context.holder.addBinding(this.bindings.createForBinding(block, parsed.items, context.params));
|
|
1758
1853
|
return block;
|
|
@@ -1840,9 +1935,10 @@ class Mounter {
|
|
|
1840
1935
|
if (this.ssg) {
|
|
1841
1936
|
fragment.insertBefore(document.createComment(ROW_MARK), fragment.firstChild);
|
|
1842
1937
|
}
|
|
1843
|
-
row.nodes = Array.from(fragment.childNodes);
|
|
1844
1938
|
this.bindings.updateRow(block.owner, row);
|
|
1845
1939
|
row.instances.forEach((child) => this.bindings.updateTree(child));
|
|
1940
|
+
await Promise.all(row.blocks.map((nested) => nested.settled()));
|
|
1941
|
+
row.nodes = Array.from(fragment.childNodes);
|
|
1846
1942
|
return row;
|
|
1847
1943
|
}
|
|
1848
1944
|
connected() {
|
|
@@ -2248,7 +2344,7 @@ function isBlockAnchor(node) {
|
|
|
2248
2344
|
if (node.nodeType !== 8)
|
|
2249
2345
|
return false;
|
|
2250
2346
|
const data = node.data.trim();
|
|
2251
|
-
return data.startsWith("for:") || data.startsWith("if:");
|
|
2347
|
+
return data.startsWith("for:") || data.startsWith("if:") || data.startsWith("tag:");
|
|
2252
2348
|
}
|
|
2253
2349
|
function isInsignificant(node) {
|
|
2254
2350
|
return node.nodeType === 3 && !(node.textContent ?? "").trim();
|
package/dist/ssg.js
CHANGED
|
@@ -590,7 +590,7 @@ class BindingEvaluator {
|
|
|
590
590
|
}
|
|
591
591
|
if (binding.kind === "prop") {
|
|
592
592
|
const target = binding.target;
|
|
593
|
-
if (
|
|
593
|
+
if (sameValue(target.props[binding.name], value))
|
|
594
594
|
return;
|
|
595
595
|
target.props[binding.name] = value;
|
|
596
596
|
this.onPropChanged(target);
|
|
@@ -703,6 +703,26 @@ function setHidden(el, hidden) {
|
|
|
703
703
|
else
|
|
704
704
|
el.removeAttribute(HIDDEN_ATTR);
|
|
705
705
|
}
|
|
706
|
+
function sameValue(a, b, depth = 0) {
|
|
707
|
+
if (Object.is(a, b))
|
|
708
|
+
return true;
|
|
709
|
+
if (depth > 20 || !isPlain(a) || !isPlain(b))
|
|
710
|
+
return false;
|
|
711
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
712
|
+
return false;
|
|
713
|
+
const keys = Object.keys(a);
|
|
714
|
+
if (keys.length !== Object.keys(b).length)
|
|
715
|
+
return false;
|
|
716
|
+
return keys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && sameValue(a[key], b[key], depth + 1));
|
|
717
|
+
}
|
|
718
|
+
function isPlain(value) {
|
|
719
|
+
if (value === null || typeof value !== "object")
|
|
720
|
+
return false;
|
|
721
|
+
if (Array.isArray(value))
|
|
722
|
+
return true;
|
|
723
|
+
const proto = Object.getPrototypeOf(value);
|
|
724
|
+
return proto === Object.prototype || proto === null;
|
|
725
|
+
}
|
|
706
726
|
// src/runtime/events/EventBinder.ts
|
|
707
727
|
class EventBinder {
|
|
708
728
|
bind(el, eventType, handlerName, owner, cleanupTarget = owner) {
|
|
@@ -1039,6 +1059,30 @@ class Row {
|
|
|
1039
1059
|
this.locals[this.locals.length - 1] = value;
|
|
1040
1060
|
}
|
|
1041
1061
|
}
|
|
1062
|
+
var TAG_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
1063
|
+
var REFUSED_TAGS = new Set([
|
|
1064
|
+
"script",
|
|
1065
|
+
"style",
|
|
1066
|
+
"template",
|
|
1067
|
+
"slot",
|
|
1068
|
+
"iframe",
|
|
1069
|
+
"object",
|
|
1070
|
+
"embed"
|
|
1071
|
+
]);
|
|
1072
|
+
function retag(source, name) {
|
|
1073
|
+
const doc = source.ownerDocument;
|
|
1074
|
+
const el = source.namespaceURI ? doc.createElementNS(source.namespaceURI, name) : doc.createElement(name);
|
|
1075
|
+
for (const attr of Array.from(source.attributes)) {
|
|
1076
|
+
if (attr.namespaceURI)
|
|
1077
|
+
el.setAttributeNS(attr.namespaceURI, attr.name, attr.value);
|
|
1078
|
+
else
|
|
1079
|
+
el.setAttribute(attr.name, attr.value);
|
|
1080
|
+
}
|
|
1081
|
+
for (const child of Array.from(source.childNodes)) {
|
|
1082
|
+
el.appendChild(child.cloneNode(true));
|
|
1083
|
+
}
|
|
1084
|
+
return el;
|
|
1085
|
+
}
|
|
1042
1086
|
function toIterable(value) {
|
|
1043
1087
|
if (value == null)
|
|
1044
1088
|
return [];
|
|
@@ -1080,12 +1124,13 @@ class ForBlock {
|
|
|
1080
1124
|
renderer;
|
|
1081
1125
|
conditional;
|
|
1082
1126
|
itemsExpr;
|
|
1127
|
+
tagged;
|
|
1083
1128
|
rows = new Map;
|
|
1084
1129
|
order = [];
|
|
1085
1130
|
pendingItems = null;
|
|
1086
1131
|
running = false;
|
|
1087
1132
|
warnedDuplicates = false;
|
|
1088
|
-
constructor(owner, anchor, templates, alias, params, parentLocals, keyFor, renderer, conditional = false, itemsExpr = "") {
|
|
1133
|
+
constructor(owner, anchor, templates, alias, params, parentLocals, keyFor, renderer, conditional = false, itemsExpr = "", tagged = false) {
|
|
1089
1134
|
this.owner = owner;
|
|
1090
1135
|
this.anchor = anchor;
|
|
1091
1136
|
this.templates = templates;
|
|
@@ -1096,22 +1141,55 @@ class ForBlock {
|
|
|
1096
1141
|
this.renderer = renderer;
|
|
1097
1142
|
this.conditional = conditional;
|
|
1098
1143
|
this.itemsExpr = itemsExpr;
|
|
1144
|
+
this.tagged = tagged;
|
|
1099
1145
|
}
|
|
1146
|
+
retagged = new Map;
|
|
1147
|
+
warnedTag = false;
|
|
1100
1148
|
toItems(value) {
|
|
1101
1149
|
if (this.conditional) {
|
|
1102
1150
|
return Number.isInteger(value) && value >= 0 ? [value] : [];
|
|
1103
1151
|
}
|
|
1152
|
+
if (this.tagged)
|
|
1153
|
+
return [this.tagFor(value)];
|
|
1104
1154
|
return toIterable(value);
|
|
1105
1155
|
}
|
|
1106
1156
|
templateFor(item) {
|
|
1157
|
+
if (this.tagged) {
|
|
1158
|
+
const source = this.templates[0];
|
|
1159
|
+
if (item === source.localName)
|
|
1160
|
+
return source;
|
|
1161
|
+
let made = this.retagged.get(item);
|
|
1162
|
+
if (!made) {
|
|
1163
|
+
made = retag(source, item);
|
|
1164
|
+
this.retagged.set(item, made);
|
|
1165
|
+
}
|
|
1166
|
+
return made;
|
|
1167
|
+
}
|
|
1107
1168
|
return this.conditional ? this.templates[item] : this.templates[0];
|
|
1108
1169
|
}
|
|
1170
|
+
tagFor(value) {
|
|
1171
|
+
const written = this.templates[0].localName;
|
|
1172
|
+
if (value == null || value === "")
|
|
1173
|
+
return written;
|
|
1174
|
+
const name = typeof value === "string" ? value.trim().toLowerCase() : null;
|
|
1175
|
+
if (name && TAG_NAME.test(name) && !REFUSED_TAGS.has(name))
|
|
1176
|
+
return name;
|
|
1177
|
+
if (!this.warnedTag) {
|
|
1178
|
+
this.warnedTag = true;
|
|
1179
|
+
warn(`$tag="${this.itemsExpr}" gave ${JSON.stringify(value)}, which is not an element it can make - rendering <${written}>.`);
|
|
1180
|
+
}
|
|
1181
|
+
return written;
|
|
1182
|
+
}
|
|
1109
1183
|
schedule(items) {
|
|
1110
1184
|
this.pendingItems = items;
|
|
1111
1185
|
if (this.running)
|
|
1112
1186
|
return;
|
|
1113
1187
|
this.running = true;
|
|
1114
|
-
this.drain();
|
|
1188
|
+
this.draining = this.drain();
|
|
1189
|
+
}
|
|
1190
|
+
draining = null;
|
|
1191
|
+
settled() {
|
|
1192
|
+
return this.draining ?? Promise.resolve();
|
|
1115
1193
|
}
|
|
1116
1194
|
async drain() {
|
|
1117
1195
|
try {
|
|
@@ -1469,6 +1547,15 @@ class Mounter {
|
|
|
1469
1547
|
this.createBlock(el, context, "if");
|
|
1470
1548
|
return;
|
|
1471
1549
|
}
|
|
1550
|
+
if (el.hasAttribute("data-bind-tag")) {
|
|
1551
|
+
if (el.tagName === "TEMPLATE") {
|
|
1552
|
+
warn("$tag on a <template> means nothing - it renders its contents, not itself.", el);
|
|
1553
|
+
el.removeAttribute("data-bind-tag");
|
|
1554
|
+
} else {
|
|
1555
|
+
this.createBlock(el, context, "tag");
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1472
1559
|
if (el.hasAttribute("data-bind-else") || el.hasAttribute("data-bind-else-if")) {
|
|
1473
1560
|
warn("$else / $else-if must directly follow a $if or $else-if sibling", el);
|
|
1474
1561
|
el.remove();
|
|
@@ -1614,6 +1701,13 @@ class Mounter {
|
|
|
1614
1701
|
i += consumed.templates - 1;
|
|
1615
1702
|
continue;
|
|
1616
1703
|
}
|
|
1704
|
+
if (el.hasAttribute("data-bind-tag") && el.tagName !== "TEMPLATE") {
|
|
1705
|
+
const consumed = this.adoptBlock(el, found, d, context, "tag");
|
|
1706
|
+
if (!consumed)
|
|
1707
|
+
return false;
|
|
1708
|
+
d += consumed.dom;
|
|
1709
|
+
continue;
|
|
1710
|
+
}
|
|
1617
1711
|
if (el.tagName === "SLOT") {
|
|
1618
1712
|
if (!found[d] || !isSlotStart(found[d])) {
|
|
1619
1713
|
if (!this.adoptNodes(Array.from(el.childNodes), found.slice(d), context, markers)) {
|
|
@@ -1749,18 +1843,19 @@ class Mounter {
|
|
|
1749
1843
|
const rawExpression = el.getAttribute(`data-bind-${kind}`) ?? "";
|
|
1750
1844
|
const branches = conditional ? this.gatherBranches(el) : [];
|
|
1751
1845
|
const expression = conditional ? composeBranchExpression(branches) : rawExpression;
|
|
1752
|
-
const parsed = conditional ? { alias: null, items: expression } : parseForExpression(rawExpression);
|
|
1846
|
+
const parsed = conditional || kind === "tag" ? { alias: null, items: expression } : parseForExpression(rawExpression);
|
|
1753
1847
|
if (!parsed) {
|
|
1754
1848
|
warn(`Could not parse $for="${expression}" - expected "item in items"`);
|
|
1755
1849
|
el.remove();
|
|
1756
1850
|
return null;
|
|
1757
1851
|
}
|
|
1758
|
-
if (
|
|
1852
|
+
if (kind === "for" && el.hasAttribute("data-bind-if")) {
|
|
1759
1853
|
warn(`$for and $if on the same element are not supported (${rawExpression}). Wrap one in an element.`);
|
|
1760
1854
|
}
|
|
1761
|
-
const keyExpr =
|
|
1855
|
+
const keyExpr = kind === "for" ? el.getAttribute("data-bind-key") : null;
|
|
1762
1856
|
el.removeAttribute(`data-bind-${kind}`);
|
|
1763
|
-
|
|
1857
|
+
if (kind === "for")
|
|
1858
|
+
el.removeAttribute("data-bind-key");
|
|
1764
1859
|
let anchor;
|
|
1765
1860
|
if (existingAnchor) {
|
|
1766
1861
|
anchor = existingAnchor;
|
|
@@ -1775,7 +1870,7 @@ class Mounter {
|
|
|
1775
1870
|
const compiled = this.bindings.compileExpression(keyExpr, params);
|
|
1776
1871
|
return compiled(context.owner.state, context.owner.props, this.cellsProxy(), this.bindings.pendingProbe(), ...locals);
|
|
1777
1872
|
} : null;
|
|
1778
|
-
const block = new ForBlock(context.owner, anchor, templates, parsed.alias, context.params, context.locals, keyFor, this, conditional, parsed.items);
|
|
1873
|
+
const block = new ForBlock(context.owner, anchor, templates, parsed.alias, context.params, context.locals, keyFor, this, conditional, parsed.items, kind === "tag");
|
|
1779
1874
|
context.holder.addBlock(block);
|
|
1780
1875
|
context.holder.addBinding(this.bindings.createForBinding(block, parsed.items, context.params));
|
|
1781
1876
|
return block;
|
|
@@ -1863,9 +1958,10 @@ class Mounter {
|
|
|
1863
1958
|
if (this.ssg) {
|
|
1864
1959
|
fragment.insertBefore(document.createComment(ROW_MARK), fragment.firstChild);
|
|
1865
1960
|
}
|
|
1866
|
-
row.nodes = Array.from(fragment.childNodes);
|
|
1867
1961
|
this.bindings.updateRow(block.owner, row);
|
|
1868
1962
|
row.instances.forEach((child) => this.bindings.updateTree(child));
|
|
1963
|
+
await Promise.all(row.blocks.map((nested) => nested.settled()));
|
|
1964
|
+
row.nodes = Array.from(fragment.childNodes);
|
|
1869
1965
|
return row;
|
|
1870
1966
|
}
|
|
1871
1967
|
connected() {
|
|
@@ -2271,7 +2367,7 @@ function isBlockAnchor(node) {
|
|
|
2271
2367
|
if (node.nodeType !== 8)
|
|
2272
2368
|
return false;
|
|
2273
2369
|
const data = node.data.trim();
|
|
2274
|
-
return data.startsWith("for:") || data.startsWith("if:");
|
|
2370
|
+
return data.startsWith("for:") || data.startsWith("if:") || data.startsWith("tag:");
|
|
2275
2371
|
}
|
|
2276
2372
|
function isInsignificant(node) {
|
|
2277
2373
|
return node.nodeType === 3 && !(node.textContent ?? "").trim();
|
|
@@ -3161,7 +3257,14 @@ function serializeNodes(nodes) {
|
|
|
3161
3257
|
|
|
3162
3258
|
// src/ssg/render.ts
|
|
3163
3259
|
var MAX_BUILD_ROUNDS = 6;
|
|
3164
|
-
|
|
3260
|
+
var queue = Promise.resolve();
|
|
3261
|
+
function renderPage(options) {
|
|
3262
|
+
const next = queue.then(() => renderPageNow(options));
|
|
3263
|
+
queue = next.catch(() => {
|
|
3264
|
+
});
|
|
3265
|
+
return next;
|
|
3266
|
+
}
|
|
3267
|
+
async function renderPageNow(options) {
|
|
3165
3268
|
const jsdom = await loadJsdom();
|
|
3166
3269
|
if (!jsdom) {
|
|
3167
3270
|
return {
|
|
@@ -73,6 +73,8 @@ export declare class ForBlock {
|
|
|
73
73
|
conditional: boolean;
|
|
74
74
|
/** The collection expression, kept so adoption can re-evaluate it. */
|
|
75
75
|
itemsExpr: string;
|
|
76
|
+
/** `$tag`: one row, of the element the expression names (ADR-063). */
|
|
77
|
+
tagged: boolean;
|
|
76
78
|
rows: Map<string, Row>;
|
|
77
79
|
order: string[];
|
|
78
80
|
private pendingItems;
|
|
@@ -93,7 +95,12 @@ export declare class ForBlock {
|
|
|
93
95
|
/** `$if`: zero or one row, driven by a condition rather than a list. */
|
|
94
96
|
conditional?: boolean,
|
|
95
97
|
/** The collection expression, kept so adoption can re-evaluate it. */
|
|
96
|
-
itemsExpr?: string
|
|
98
|
+
itemsExpr?: string,
|
|
99
|
+
/** `$tag`: one row, of the element the expression names (ADR-063). */
|
|
100
|
+
tagged?: boolean);
|
|
101
|
+
/** `$tag`'s copies of the template under other names, made once each. */
|
|
102
|
+
private retagged;
|
|
103
|
+
private warnedTag;
|
|
97
104
|
/**
|
|
98
105
|
* Turn a bound expression into the rows it implies.
|
|
99
106
|
*
|
|
@@ -103,6 +110,8 @@ export declare class ForBlock {
|
|
|
103
110
|
toItems(value: any): any[];
|
|
104
111
|
/** The markup a row should clone. */
|
|
105
112
|
templateFor(item: any): Element;
|
|
113
|
+
/** The element `$tag` evaluated to, or the one as written. */
|
|
114
|
+
private tagFor;
|
|
106
115
|
/**
|
|
107
116
|
* Queue a reconcile.
|
|
108
117
|
*
|
|
@@ -111,6 +120,14 @@ export declare class ForBlock {
|
|
|
111
120
|
* matters, so it replaces any queued one and the loop runs until settled.
|
|
112
121
|
*/
|
|
113
122
|
schedule(items: any[]): void;
|
|
123
|
+
private draining;
|
|
124
|
+
/**
|
|
125
|
+
* Resolves once every queued reconcile has run.
|
|
126
|
+
*
|
|
127
|
+
* A row built inside another block's new row waits for this before that row
|
|
128
|
+
* counts its nodes, or the rows placed meanwhile are left out of it.
|
|
129
|
+
*/
|
|
130
|
+
settled(): Promise<void>;
|
|
114
131
|
private drain;
|
|
115
132
|
/** Public so adoption can match rendered rows to the current data. */
|
|
116
133
|
keysFor(items: any[]): string[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/vite.js
CHANGED
|
@@ -329,7 +329,7 @@ class BindingEvaluator {
|
|
|
329
329
|
}
|
|
330
330
|
if (binding.kind === "prop") {
|
|
331
331
|
const target = binding.target;
|
|
332
|
-
if (
|
|
332
|
+
if (sameValue(target.props[binding.name], value))
|
|
333
333
|
return;
|
|
334
334
|
target.props[binding.name] = value;
|
|
335
335
|
this.onPropChanged(target);
|
|
@@ -442,6 +442,26 @@ function setHidden(el, hidden) {
|
|
|
442
442
|
else
|
|
443
443
|
el.removeAttribute(HIDDEN_ATTR);
|
|
444
444
|
}
|
|
445
|
+
function sameValue(a, b, depth = 0) {
|
|
446
|
+
if (Object.is(a, b))
|
|
447
|
+
return true;
|
|
448
|
+
if (depth > 20 || !isPlain(a) || !isPlain(b))
|
|
449
|
+
return false;
|
|
450
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
451
|
+
return false;
|
|
452
|
+
const keys = Object.keys(a);
|
|
453
|
+
if (keys.length !== Object.keys(b).length)
|
|
454
|
+
return false;
|
|
455
|
+
return keys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && sameValue(a[key], b[key], depth + 1));
|
|
456
|
+
}
|
|
457
|
+
function isPlain(value) {
|
|
458
|
+
if (value === null || typeof value !== "object")
|
|
459
|
+
return false;
|
|
460
|
+
if (Array.isArray(value))
|
|
461
|
+
return true;
|
|
462
|
+
const proto = Object.getPrototypeOf(value);
|
|
463
|
+
return proto === Object.prototype || proto === null;
|
|
464
|
+
}
|
|
445
465
|
// src/runtime/dom/ForBlock.ts
|
|
446
466
|
class Row {
|
|
447
467
|
locals;
|
|
@@ -470,6 +490,30 @@ class Row {
|
|
|
470
490
|
this.locals[this.locals.length - 1] = value;
|
|
471
491
|
}
|
|
472
492
|
}
|
|
493
|
+
var TAG_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
494
|
+
var REFUSED_TAGS = new Set([
|
|
495
|
+
"script",
|
|
496
|
+
"style",
|
|
497
|
+
"template",
|
|
498
|
+
"slot",
|
|
499
|
+
"iframe",
|
|
500
|
+
"object",
|
|
501
|
+
"embed"
|
|
502
|
+
]);
|
|
503
|
+
function retag(source, name) {
|
|
504
|
+
const doc = source.ownerDocument;
|
|
505
|
+
const el = source.namespaceURI ? doc.createElementNS(source.namespaceURI, name) : doc.createElement(name);
|
|
506
|
+
for (const attr of Array.from(source.attributes)) {
|
|
507
|
+
if (attr.namespaceURI)
|
|
508
|
+
el.setAttributeNS(attr.namespaceURI, attr.name, attr.value);
|
|
509
|
+
else
|
|
510
|
+
el.setAttribute(attr.name, attr.value);
|
|
511
|
+
}
|
|
512
|
+
for (const child of Array.from(source.childNodes)) {
|
|
513
|
+
el.appendChild(child.cloneNode(true));
|
|
514
|
+
}
|
|
515
|
+
return el;
|
|
516
|
+
}
|
|
473
517
|
function toIterable(value) {
|
|
474
518
|
if (value == null)
|
|
475
519
|
return [];
|
|
@@ -511,12 +555,13 @@ class ForBlock {
|
|
|
511
555
|
renderer;
|
|
512
556
|
conditional;
|
|
513
557
|
itemsExpr;
|
|
558
|
+
tagged;
|
|
514
559
|
rows = new Map;
|
|
515
560
|
order = [];
|
|
516
561
|
pendingItems = null;
|
|
517
562
|
running = false;
|
|
518
563
|
warnedDuplicates = false;
|
|
519
|
-
constructor(owner, anchor, templates, alias, params, parentLocals, keyFor, renderer, conditional = false, itemsExpr = "") {
|
|
564
|
+
constructor(owner, anchor, templates, alias, params, parentLocals, keyFor, renderer, conditional = false, itemsExpr = "", tagged = false) {
|
|
520
565
|
this.owner = owner;
|
|
521
566
|
this.anchor = anchor;
|
|
522
567
|
this.templates = templates;
|
|
@@ -527,22 +572,55 @@ class ForBlock {
|
|
|
527
572
|
this.renderer = renderer;
|
|
528
573
|
this.conditional = conditional;
|
|
529
574
|
this.itemsExpr = itemsExpr;
|
|
575
|
+
this.tagged = tagged;
|
|
530
576
|
}
|
|
577
|
+
retagged = new Map;
|
|
578
|
+
warnedTag = false;
|
|
531
579
|
toItems(value) {
|
|
532
580
|
if (this.conditional) {
|
|
533
581
|
return Number.isInteger(value) && value >= 0 ? [value] : [];
|
|
534
582
|
}
|
|
583
|
+
if (this.tagged)
|
|
584
|
+
return [this.tagFor(value)];
|
|
535
585
|
return toIterable(value);
|
|
536
586
|
}
|
|
537
587
|
templateFor(item) {
|
|
588
|
+
if (this.tagged) {
|
|
589
|
+
const source = this.templates[0];
|
|
590
|
+
if (item === source.localName)
|
|
591
|
+
return source;
|
|
592
|
+
let made = this.retagged.get(item);
|
|
593
|
+
if (!made) {
|
|
594
|
+
made = retag(source, item);
|
|
595
|
+
this.retagged.set(item, made);
|
|
596
|
+
}
|
|
597
|
+
return made;
|
|
598
|
+
}
|
|
538
599
|
return this.conditional ? this.templates[item] : this.templates[0];
|
|
539
600
|
}
|
|
601
|
+
tagFor(value) {
|
|
602
|
+
const written = this.templates[0].localName;
|
|
603
|
+
if (value == null || value === "")
|
|
604
|
+
return written;
|
|
605
|
+
const name = typeof value === "string" ? value.trim().toLowerCase() : null;
|
|
606
|
+
if (name && TAG_NAME.test(name) && !REFUSED_TAGS.has(name))
|
|
607
|
+
return name;
|
|
608
|
+
if (!this.warnedTag) {
|
|
609
|
+
this.warnedTag = true;
|
|
610
|
+
warn(`$tag="${this.itemsExpr}" gave ${JSON.stringify(value)}, which is not an element it can make - rendering <${written}>.`);
|
|
611
|
+
}
|
|
612
|
+
return written;
|
|
613
|
+
}
|
|
540
614
|
schedule(items) {
|
|
541
615
|
this.pendingItems = items;
|
|
542
616
|
if (this.running)
|
|
543
617
|
return;
|
|
544
618
|
this.running = true;
|
|
545
|
-
this.drain();
|
|
619
|
+
this.draining = this.drain();
|
|
620
|
+
}
|
|
621
|
+
draining = null;
|
|
622
|
+
settled() {
|
|
623
|
+
return this.draining ?? Promise.resolve();
|
|
546
624
|
}
|
|
547
625
|
async drain() {
|
|
548
626
|
try {
|
|
@@ -900,6 +978,15 @@ class Mounter {
|
|
|
900
978
|
this.createBlock(el, context, "if");
|
|
901
979
|
return;
|
|
902
980
|
}
|
|
981
|
+
if (el.hasAttribute("data-bind-tag")) {
|
|
982
|
+
if (el.tagName === "TEMPLATE") {
|
|
983
|
+
warn("$tag on a <template> means nothing - it renders its contents, not itself.", el);
|
|
984
|
+
el.removeAttribute("data-bind-tag");
|
|
985
|
+
} else {
|
|
986
|
+
this.createBlock(el, context, "tag");
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
903
990
|
if (el.hasAttribute("data-bind-else") || el.hasAttribute("data-bind-else-if")) {
|
|
904
991
|
warn("$else / $else-if must directly follow a $if or $else-if sibling", el);
|
|
905
992
|
el.remove();
|
|
@@ -1045,6 +1132,13 @@ class Mounter {
|
|
|
1045
1132
|
i += consumed.templates - 1;
|
|
1046
1133
|
continue;
|
|
1047
1134
|
}
|
|
1135
|
+
if (el.hasAttribute("data-bind-tag") && el.tagName !== "TEMPLATE") {
|
|
1136
|
+
const consumed = this.adoptBlock(el, found, d, context, "tag");
|
|
1137
|
+
if (!consumed)
|
|
1138
|
+
return false;
|
|
1139
|
+
d += consumed.dom;
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1048
1142
|
if (el.tagName === "SLOT") {
|
|
1049
1143
|
if (!found[d] || !isSlotStart(found[d])) {
|
|
1050
1144
|
if (!this.adoptNodes(Array.from(el.childNodes), found.slice(d), context, markers)) {
|
|
@@ -1180,18 +1274,19 @@ class Mounter {
|
|
|
1180
1274
|
const rawExpression = el.getAttribute(`data-bind-${kind}`) ?? "";
|
|
1181
1275
|
const branches = conditional ? this.gatherBranches(el) : [];
|
|
1182
1276
|
const expression = conditional ? composeBranchExpression(branches) : rawExpression;
|
|
1183
|
-
const parsed = conditional ? { alias: null, items: expression } : parseForExpression(rawExpression);
|
|
1277
|
+
const parsed = conditional || kind === "tag" ? { alias: null, items: expression } : parseForExpression(rawExpression);
|
|
1184
1278
|
if (!parsed) {
|
|
1185
1279
|
warn(`Could not parse $for="${expression}" - expected "item in items"`);
|
|
1186
1280
|
el.remove();
|
|
1187
1281
|
return null;
|
|
1188
1282
|
}
|
|
1189
|
-
if (
|
|
1283
|
+
if (kind === "for" && el.hasAttribute("data-bind-if")) {
|
|
1190
1284
|
warn(`$for and $if on the same element are not supported (${rawExpression}). Wrap one in an element.`);
|
|
1191
1285
|
}
|
|
1192
|
-
const keyExpr =
|
|
1286
|
+
const keyExpr = kind === "for" ? el.getAttribute("data-bind-key") : null;
|
|
1193
1287
|
el.removeAttribute(`data-bind-${kind}`);
|
|
1194
|
-
|
|
1288
|
+
if (kind === "for")
|
|
1289
|
+
el.removeAttribute("data-bind-key");
|
|
1195
1290
|
let anchor;
|
|
1196
1291
|
if (existingAnchor) {
|
|
1197
1292
|
anchor = existingAnchor;
|
|
@@ -1206,7 +1301,7 @@ class Mounter {
|
|
|
1206
1301
|
const compiled = this.bindings.compileExpression(keyExpr, params);
|
|
1207
1302
|
return compiled(context.owner.state, context.owner.props, this.cellsProxy(), this.bindings.pendingProbe(), ...locals);
|
|
1208
1303
|
} : null;
|
|
1209
|
-
const block = new ForBlock(context.owner, anchor, templates, parsed.alias, context.params, context.locals, keyFor, this, conditional, parsed.items);
|
|
1304
|
+
const block = new ForBlock(context.owner, anchor, templates, parsed.alias, context.params, context.locals, keyFor, this, conditional, parsed.items, kind === "tag");
|
|
1210
1305
|
context.holder.addBlock(block);
|
|
1211
1306
|
context.holder.addBinding(this.bindings.createForBinding(block, parsed.items, context.params));
|
|
1212
1307
|
return block;
|
|
@@ -1294,9 +1389,10 @@ class Mounter {
|
|
|
1294
1389
|
if (this.ssg) {
|
|
1295
1390
|
fragment.insertBefore(document.createComment(ROW_MARK), fragment.firstChild);
|
|
1296
1391
|
}
|
|
1297
|
-
row.nodes = Array.from(fragment.childNodes);
|
|
1298
1392
|
this.bindings.updateRow(block.owner, row);
|
|
1299
1393
|
row.instances.forEach((child) => this.bindings.updateTree(child));
|
|
1394
|
+
await Promise.all(row.blocks.map((nested) => nested.settled()));
|
|
1395
|
+
row.nodes = Array.from(fragment.childNodes);
|
|
1300
1396
|
return row;
|
|
1301
1397
|
}
|
|
1302
1398
|
connected() {
|
|
@@ -1702,7 +1798,7 @@ function isBlockAnchor(node) {
|
|
|
1702
1798
|
if (node.nodeType !== 8)
|
|
1703
1799
|
return false;
|
|
1704
1800
|
const data = node.data.trim();
|
|
1705
|
-
return data.startsWith("for:") || data.startsWith("if:");
|
|
1801
|
+
return data.startsWith("for:") || data.startsWith("if:") || data.startsWith("tag:");
|
|
1706
1802
|
}
|
|
1707
1803
|
function isInsignificant(node) {
|
|
1708
1804
|
return node.nodeType === 3 && !(node.textContent ?? "").trim();
|
|
@@ -4552,7 +4648,14 @@ function serializeNodes(nodes) {
|
|
|
4552
4648
|
|
|
4553
4649
|
// src/ssg/render.ts
|
|
4554
4650
|
var MAX_BUILD_ROUNDS = 6;
|
|
4555
|
-
|
|
4651
|
+
var queue = Promise.resolve();
|
|
4652
|
+
function renderPage(options) {
|
|
4653
|
+
const next = queue.then(() => renderPageNow(options));
|
|
4654
|
+
queue = next.catch(() => {
|
|
4655
|
+
});
|
|
4656
|
+
return next;
|
|
4657
|
+
}
|
|
4658
|
+
async function renderPageNow(options) {
|
|
4556
4659
|
const jsdom = await loadJsdom();
|
|
4557
4660
|
if (!jsdom) {
|
|
4558
4661
|
return {
|