malinajs 0.7.2-a1 → 0.7.2-a11

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/malina.js CHANGED
@@ -290,6 +290,7 @@
290
290
 
291
291
  this.isEmpty = function(n) {
292
292
  if(n == null) return true;
293
+ if(n.$type == 'if:bind') return false;
293
294
  assert(n.$done, 'Node is not built');
294
295
  return !n.$result.some(r => {
295
296
  if(typeof (r) == 'string') return true;
@@ -712,6 +713,7 @@
712
713
  switch(node.type) {
713
714
  case 'node':
714
715
  case 'slot':
716
+ case 'block':
715
717
  case 'fragment':
716
718
  if(node.body) go(node.body, node);
717
719
  break
@@ -761,8 +763,9 @@
761
763
  if(prev && next) {
762
764
  if(prev.type == 'node' && next.type == 'node') {
763
765
  if(isTable(prev) && isTable(next) ||
764
- prev.name == 'li' && next.name == 'li' ||
765
- prev.name == 'div' && next.name == 'div') {
766
+ prev.name == 'li' && next.name == 'li' ||
767
+ parentNode?.type == 'node' && parentNode?.name == 'select' && prev.name == 'option' && next.name == 'option' ||
768
+ prev.name == 'div' && next.name == 'div') {
766
769
  body.splice(i, 1);
767
770
  continue;
768
771
  }
@@ -783,6 +786,10 @@
783
786
  body.splice(i, 1);
784
787
  continue;
785
788
  }
789
+ if((p == 'option' || n == 'option') && (parentNode.type == 'node' && parentNode.name == 'select')) {
790
+ body.splice(i, 1);
791
+ continue;
792
+ }
786
793
  if(parentNode.type == 'node' && ['div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7'].includes(parentNode.name)) {
787
794
  body.splice(i, 1);
788
795
  continue;
@@ -834,121 +841,103 @@
834
841
  go(data.body);
835
842
  }
836
843
 
837
- function parse() {
838
- let source = this.source;
839
- let index = 0;
844
+ class Reader {
845
+ constructor(source) {
846
+ if(source instanceof Reader) return source;
847
+ this.index = 0;
848
+ this.source = source;
849
+ }
840
850
 
841
- const readNext = () => {
842
- assert(index < source.length, 'EOF');
843
- return source[index++];
844
- };
851
+ read(pattern) {
852
+ assert(!this.end(), 'EOF');
853
+ if(pattern == null) {
854
+ return this.source[this.index++];
855
+ } else if(pattern instanceof RegExp) {
856
+ assert(pattern.source[0] == '^');
857
+ const rx = this.source.substring(this.index).match(pattern);
858
+ assert(rx && rx.index == 0, 'Wrong syntax');
859
+ let r = rx[1] || rx[0];
860
+ this.index += rx[0].length;
861
+ return r;
862
+ } else throw 'Not implemented';
863
+ }
845
864
 
846
- const probeNext = () => source[index];
865
+ probe(pattern) {
866
+ if(pattern instanceof RegExp) {
867
+ assert(pattern.source[0] == '^');
868
+ const r = this.source.substring(this.index).match(pattern);
869
+ if(r) return r[0];
870
+ } else {
871
+ if(this.source[this.index] == pattern[0] && this.source.substr(this.index, pattern.length) == pattern) return pattern;
872
+ }
873
+ return null;
874
+ }
847
875
 
848
- const readTag = () => {
849
- let start = index;
850
- let a = readNext();
851
- assert(a === '<', 'Tag error');
852
- let attributes = [];
853
- let begin = true;
854
- let name = '';
855
- let eq, attr_start;
856
- let elArg = null;
876
+ probeQuote() {
877
+ const a = this.source[this.index];
878
+ return a == '"' || a == "'" || a == '`';
879
+ }
857
880
 
858
- const error = (name) => {
859
- let e = new Error(name);
860
- e.details = source.substring(start, index);
861
- throw e;
862
- };
881
+ readIf(pattern) {
882
+ const r = this.probe(pattern);
883
+ if(r != null) this.index += r.length;
884
+ return r;
885
+ }
863
886
 
864
- function flush(shift) {
865
- if(!attr_start) return;
866
- shift = shift || 0;
867
- let end = index - 1 + shift;
868
- if(elArg === true) {
869
- elArg = source.substring(attr_start, end);
870
- attr_start = null;
871
- eq = null;
872
- return;
873
- }
874
- let a = {
875
- content: source.substring(attr_start, end)
876
- };
877
- if(eq) {
878
- a.name = source.substring(attr_start, eq);
879
- a.value = source.substring(eq + 1, end);
880
- if(a.value[0] == '"' || a.value[0] == '\'') a.value = a.value.substring(1);
881
- let i = a.value.length - 1;
882
- if(a.value[i] == '"' || a.value[i] == '\'') a.value = a.value.substring(0, i);
883
- } else a.name = a.content;
884
- attributes.push(a);
885
- attr_start = null;
886
- eq = null;
887
+ end() {
888
+ return this.index >= this.source.length;
889
+ }
890
+
891
+ skip() {
892
+ while(!this.end()) {
893
+ if(!this.source[this.index].match(/\s/)) break;
894
+ this.index++;
887
895
  }
896
+ }
888
897
 
898
+ readString() {
899
+ let q = this.read();
900
+ assert(q == '"' || q == '`' || q == `'`, 'Wrong syntax');
901
+ let a = null, p, result = q;
889
902
  while(true) {
890
- a = readNext();
891
- if(!begin && !attr_start && a.match(/\S/) && a != '/' && a != '>') attr_start = index - 1;
892
- if(a == '"' || a == "'" || a == '`') {
893
- while(a != readNext());
894
- continue;
895
- }
896
- if(a == '{') {
897
- index--;
898
- readBinding();
899
- flush(1);
900
- continue;
901
- }
902
- if(a == '}') error('Wrong attr');
903
- if(a == '<') error('Wrong tag');
904
- if(a == '/') {
905
- a = readNext();
906
- assert(a == '>');
907
- flush(-1);
908
- }
909
- if(a == '>') {
910
- flush();
911
- const voidTags = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
912
- let voidTag = voidTags.indexOf(name) >= 0;
913
- let closedTag = voidTag || source[index - 2] == '/';
914
- return {
915
- type: 'node',
916
- name,
917
- elArg,
918
- openTag: source.substring(start, index),
919
- start: start,
920
- end: index,
921
- closedTag,
922
- voidTag,
923
- attributes
924
- };
925
- }
926
- if(begin) {
927
- if(a.match(/[\da-zA-Z^\-]/)) {
928
- name += a;
929
- continue;
930
- } else {
931
- begin = false;
932
- if(a == ':') {
933
- elArg = true;
934
- attr_start = index;
935
- }
936
- }
937
- } else if(attr_start) {
938
- if(a == '=' && !eq) eq = index - 1;
939
- else if(a.match(/\s/)) flush();
940
- }
903
+ p = a;
904
+ a = this.read();
905
+ result += a;
906
+ if(a == q && p != '\\') break;
941
907
  }
942
- };
908
+ return result;
909
+ }
910
+
911
+ readAttribute() {
912
+ let name = '';
913
+ while(true) {
914
+ if(this.end()) break;
915
+ let a = this.source[this.index];
916
+ if(a == '=' || a == '/' || a == '>' || a == '\t' || a == '\n' || a == '\v' || a == '\f' || a == '\r' || a == ' ' || a == ' ') break;
917
+ name += a;
918
+ this.index++;
919
+ }
920
+ assert(name, 'Syntax error');
921
+ return name;
922
+ }
923
+
924
+ sub(start, end) {
925
+ return this.source.substring(start, end || this.index);
926
+ }
927
+ }
943
928
 
944
- const readScript = (tag) => {
945
- let endTag = `</${tag}>`;
946
- let q, a, p, start = index;
929
+ function parseHTML(source) {
930
+ const reader = new Reader(source);
931
+
932
+ const readScript = (reader) => {
933
+ const start = reader.index;
947
934
 
948
935
  const readRegExp = () => {
936
+ assert(reader.read() == '/');
937
+ let a, p, q;
949
938
  while(true) {
950
939
  p = a;
951
- a = readNext();
940
+ a = reader.read();
952
941
  if(q) {
953
942
  if(a != q) continue;
954
943
  if(p == '\\') continue;
@@ -961,108 +950,40 @@
961
950
  };
962
951
 
963
952
  while(true) {
964
- p = a;
965
- a = readNext();
966
- if(q) {
967
- if(a != q) continue;
968
- if(p == '\\') continue;
969
- q = null;
970
- continue;
971
- }
972
- if(a == '"' || a == '\'' || a == '`') {
973
- q = a;
953
+ if(reader.probeQuote()) {
954
+ reader.readString();
974
955
  continue;
975
956
  }
976
- if(a == '/') {
977
- let n = probeNext();
978
- if(n == '/') { // inline comment
979
- while(readNext() != '\n');
980
- a = null;
981
- continue
957
+ if(reader.probe('/')) {
958
+ if(reader.readIf('//')) {
959
+ while(reader.read() != '\n');
960
+ continue;
982
961
  }
983
- if(n == '*') {
984
- readNext();
985
- while(true) { // multiline comment
986
- if(readNext() == '*' && probeNext() == '/') break;
962
+ if(reader.readIf('/*')) {
963
+ while(true) {
964
+ if(reader.read() == '*' && reader.readIf('/')) break;
987
965
  }
988
- readNext();
989
- a = null;
990
966
  continue;
991
967
  }
992
968
  readRegExp();
993
969
  continue;
994
970
  }
995
- if(a == '<') {
996
- if(source.substring(index - 1, index + endTag.length - 1) == endTag) {
997
- let end = index - 1;
998
- index += endTag.length - 1;
999
- return source.substring(start, end);
1000
- }
1001
- }
971
+ if(reader.readIf('</script>')) {
972
+ return reader.sub(start, reader.index - 9);
973
+ } else reader.read();
1002
974
  }
1003
975
  };
1004
976
 
1005
977
  const readStyle = () => {
1006
- let start = index;
1007
- let end = source.substring(start).indexOf('</style>') + start;
1008
- assert(end >= 0, '<style> is not closed');
1009
- index = end + 9;
1010
- return source.substring(start, end);
978
+ return reader.read(/^(.*?)<\/style>/s);
1011
979
  };
1012
980
 
1013
- const readBinding = () => {
1014
- let start = index;
1015
- assert(readNext() === '{', 'Bind error');
1016
- let a = null, p, q;
1017
- let bkt = 1;
1018
-
1019
- while(true) {
1020
- p = a;
1021
- a = readNext();
1022
-
1023
- if(q) {
1024
- if(a != q) continue;
1025
- if(p == '\\') continue;
1026
- q = null;
1027
- continue;
1028
- }
1029
- if(a == '"' || a == "'" || a == '`') {
1030
- q = a;
1031
- continue;
1032
- }
1033
- if(a == '*' && p == '/') {
1034
- // comment block
1035
- while(true) {
1036
- p = a;
1037
- a = readNext();
1038
- if(a == '/' && p == '*') break;
1039
- }
1040
- continue;
1041
- }
1042
-
1043
- if(a == '{') {
1044
- bkt++;
1045
- continue;
1046
- }
1047
- if(a == '}') {
1048
- bkt--;
1049
- if(bkt > 0) continue;
1050
- } else continue;
1051
-
1052
- return {
1053
- value: source.substring(start + 1, index - 1),
1054
- raw: source.substring(start, index)
1055
- };
1056
- }
981
+ const readComment = () => {
982
+ return reader.read(/^<!--.*?-->/s);
1057
983
  };
1058
984
 
1059
- const readComment = () => {
1060
- let start = index;
1061
- let end = source.indexOf('-->', start);
1062
- assert(end >= 0, 'Comment is not closed');
1063
- end += 3;
1064
- index = end;
1065
- return source.substring(start, end);
985
+ const readTemplate = () => {
986
+ return reader.read(/^(.*?)<\/template>/s);
1066
987
  };
1067
988
 
1068
989
  const go = (parent, push) => {
@@ -1085,12 +1006,11 @@
1085
1006
  textNode = null;
1086
1007
  };
1087
1008
 
1088
- while(index < source.length) {
1089
- let a = source[index];
1090
- if(a === '<' && source[index + 1].match(/\S/)) {
1009
+ while(!reader.end()) {
1010
+ if(reader.probe('<') && reader.probe(/^<\S/)) {
1091
1011
  flushText();
1092
1012
 
1093
- if(source.substring(index, index + 4) === '<!--') {
1013
+ if(reader.probe('<!--')) {
1094
1014
  push({
1095
1015
  type: 'comment',
1096
1016
  content: readComment()
@@ -1098,14 +1018,8 @@
1098
1018
  continue;
1099
1019
  }
1100
1020
 
1101
- if(source[index + 1] === '/') { // close tag
1102
- let name = '';
1103
- index += 2;
1104
- while(true) {
1105
- a = readNext();
1106
- if(a === '>') break;
1107
- name += a;
1108
- }
1021
+ if(reader.readIf('</')) { // close tag
1022
+ let name = reader.read(/^([^>]*)>/);
1109
1023
  name = name.trim();
1110
1024
  if(name) {
1111
1025
  name = name.split(':')[0];
@@ -1114,15 +1028,15 @@
1114
1028
  return;
1115
1029
  }
1116
1030
 
1117
- let tag = readTag();
1031
+ let tag = readTag(reader);
1118
1032
  push(tag);
1119
1033
  if(tag.name === 'script') {
1120
1034
  tag.type = 'script';
1121
- tag.content = readScript('script');
1035
+ tag.content = readScript(reader);
1122
1036
  continue;
1123
1037
  } else if(tag.name === 'template') {
1124
1038
  tag.type = 'template';
1125
- tag.content = readScript('template');
1039
+ tag.content = readTemplate();
1126
1040
  continue;
1127
1041
  } else if(tag.name === 'style') {
1128
1042
  tag.type = 'style';
@@ -1143,9 +1057,9 @@
1143
1057
  throw e;
1144
1058
  }
1145
1059
  continue;
1146
- } else if(a === '{') {
1147
- if(['#', '/', ':', '@', '*'].indexOf(source[index + 1]) >= 0) {
1148
- let bind = readBinding();
1060
+ } else if(reader.probe('{')) {
1061
+ if(reader.probe(/^\{[#/:@*]/)) {
1062
+ let bind = parseBinding(reader);
1149
1063
  if(bind.value[0] != '*') flushText();
1150
1064
  if(bind.value[0] == '*') {
1151
1065
  addText(bind.raw);
@@ -1244,11 +1158,29 @@
1244
1158
  } else if(bind.value == '/fragment') {
1245
1159
  assert(parent.type === 'fragment', 'Fragment error: /fragment');
1246
1160
  return;
1161
+ } else if(bind.value.match(/^#([\w\-]+)/)) {
1162
+ const name = bind.value.match(/^#([\w\-]+)/)[1];
1163
+ let tag = {
1164
+ type: 'block',
1165
+ value: bind.value,
1166
+ name,
1167
+ body: []
1168
+ };
1169
+ push(tag);
1170
+ go(tag);
1171
+ continue;
1172
+ } else if(bind.value.match(/^\/([\w\-]+)/)) {
1173
+ const name = bind.value.match(/^\/([\w\-]+)/)[1];
1174
+ assert(parent.type === 'block' && parent.name == name, `Fragment error: ${parent.name} - ${name}`);
1175
+ return;
1247
1176
  } else throw 'Error binding: ' + bind.value;
1177
+ } else {
1178
+ addText(parseBinding(reader).raw);
1179
+ continue;
1248
1180
  }
1249
1181
  }
1250
1182
 
1251
- addText(readNext());
1183
+ addText(reader.read());
1252
1184
  }
1253
1185
  flushText();
1254
1186
  assert(parent.type === 'root', 'File ends to early');
@@ -1260,9 +1192,41 @@
1260
1192
  };
1261
1193
  go(root);
1262
1194
 
1263
- this.DOM = root;
1195
+ return root;
1264
1196
  }
1265
1197
 
1198
+ function readTag(reader) {
1199
+ const start = reader.index;
1200
+ assert(reader.read() === '<', 'Tag error');
1201
+
1202
+ let name = reader.read(/^[\da-zA-Z^\-]+/);
1203
+ let elArg = null;
1204
+
1205
+ if(reader.readIf(':')) {
1206
+ elArg = reader.read(/^[^\s>/]+/);
1207
+ }
1208
+
1209
+ let attributes = parseAttibutes(reader, {closedByTag: true});
1210
+
1211
+ let closedTag = false;
1212
+ if(reader.readIf('/>')) closedTag = true;
1213
+ else assert(reader.readIf('>'));
1214
+
1215
+ const voidTags = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
1216
+ let voidTag = voidTags.indexOf(name) >= 0;
1217
+ if(voidTag) closedTag = true;
1218
+ return {
1219
+ type: 'node',
1220
+ name,
1221
+ elArg,
1222
+ openTag: reader.sub(start),
1223
+ start: start,
1224
+ end: reader.index,
1225
+ closedTag,
1226
+ voidTag,
1227
+ attributes
1228
+ };
1229
+ }
1266
1230
 
1267
1231
  function parseText(source) {
1268
1232
  let i = 0;
@@ -1328,11 +1292,116 @@
1328
1292
  else result.push({ ...p });
1329
1293
  }
1330
1294
  });
1331
- result = result.map(p => p.type == 'text' ? '`' + Q(p.value) + '`' : '(' + p.value + ')').join('+');
1332
- return { result, parts, staticText };
1295
+ result = '`' + result.map(p => p.type == 'text' ? Q(p.value) : '${' + p.value + '}').join('') + '`';
1296
+ return {
1297
+ result,
1298
+ parts,
1299
+ staticText,
1300
+ binding: parts.length == 1 && parts[0].type == 'exp' ? parts[0].value : null
1301
+ };
1333
1302
  }
1334
1303
 
1335
- function parse$1() {
1304
+
1305
+ const parseBinding = (reader) => {
1306
+ let start = reader.index;
1307
+
1308
+ assert(reader.read() === '{', 'Bind error');
1309
+ let a = null, p, q;
1310
+ let bkt = 1;
1311
+
1312
+ while(true) {
1313
+ p = a;
1314
+ a = reader.read();
1315
+
1316
+ if(q) {
1317
+ if(a != q) continue;
1318
+ if(p == '\\') continue;
1319
+ q = null;
1320
+ continue;
1321
+ }
1322
+ if(a == '"' || a == "'" || a == '`') {
1323
+ q = a;
1324
+ continue;
1325
+ }
1326
+ if(a == '*' && p == '/') {
1327
+ // comment block
1328
+ while(true) {
1329
+ p = a;
1330
+ a = reader.read();
1331
+ if(a == '/' && p == '*') break;
1332
+ }
1333
+ continue;
1334
+ }
1335
+
1336
+ if(a == '{') {
1337
+ bkt++;
1338
+ continue;
1339
+ }
1340
+ if(a == '}') {
1341
+ bkt--;
1342
+ if(bkt > 0) continue;
1343
+ } else continue;
1344
+
1345
+ const raw = reader.sub(start);
1346
+ return {
1347
+ raw,
1348
+ value: raw.substring(1, raw.length - 1),
1349
+ };
1350
+ }
1351
+ };
1352
+
1353
+
1354
+ const parseAttibutes = (source, option={}) => {
1355
+ const r = new Reader(source);
1356
+ let result = [];
1357
+
1358
+ while(!r.end()) {
1359
+ r.skip();
1360
+ if(option.closedByTag) {
1361
+ if(r.probe('/>') || r.probe('>')) break;
1362
+ } else if(r.end()) break;
1363
+ let start = r.index;
1364
+ if(r.probe('{*')) {
1365
+ const {raw} = parseBinding(r);
1366
+ result.push({name: raw, content: raw});
1367
+ } else if(r.probe('*{')) {
1368
+ r.read();
1369
+ let {raw} = parseBinding(r);
1370
+ raw = '*' + raw;
1371
+ result.push({name: raw, content: raw});
1372
+ } else if(r.probe('{...')) {
1373
+ let {raw} = parseBinding(r);
1374
+ result.push({name: raw, content: raw});
1375
+ } else {
1376
+ let name = r.readAttribute();
1377
+ assert(name, 'Wrong syntax');
1378
+ if(r.readIf('=')) {
1379
+ if(r.probe('{')) {
1380
+ const {raw} = parseBinding(r);
1381
+ result.push({name, value: raw, raw, content: r.sub(start)});
1382
+ } else if(r.probeQuote()) {
1383
+ const raw = r.readString();
1384
+ const value = raw.substring(1, raw.length - 1);
1385
+ result.push({name, value, raw, content: r.sub(start)});
1386
+ } else {
1387
+ const value = r.readIf(/^[^\s<>]+/);
1388
+ result.push({name, value, raw: value, content: r.sub(start)});
1389
+ }
1390
+ } else {
1391
+ let value;
1392
+ if(name[0] == '{' && last(name) == '}' && !name.startsWith('{...')) {
1393
+ value = name;
1394
+ name = unwrapExp(name);
1395
+ }
1396
+ result.push({name, value, raw: value, content: r.sub(start)});
1397
+ }
1398
+ }
1399
+ }
1400
+
1401
+ return result;
1402
+ };
1403
+
1404
+ function parse() {
1336
1405
  let source = this.scriptNodes.length ? this.scriptNodes[0].content : null;
1337
1406
  this.script = {
1338
1407
  source,
@@ -1793,6 +1862,34 @@
1793
1862
  });
1794
1863
  };
1795
1864
 
1865
+ function radioInput(node, el) {
1866
+ assert(node.name == 'input');
1867
+ const aType = node.attributes.find(a => a.name == 'type');
1868
+ if(!aType || aType.value != 'radio') return null;
1869
+ const aName = node.attributes.find(a => a.name == 'name');
1870
+ if(!aName.value.startsWith('{')) return null;
1871
+ const aValue = node.attributes.find(a => a.name == 'value');
1872
+
1873
+ aName._skip = true;
1874
+ aValue._skip = true;
1875
+
1876
+ const name = unwrapExp(aName.value);
1877
+ assert(detectExpressionType(name) == 'identifier', 'Wrong name for radio input');
1878
+ let value = aValue.value;
1879
+ if(value.match(/^\{.+\}$/)) value = unwrapExp(aValue.value);
1880
+ else value = '`' + value + '`';
1881
+
1882
+ this.require('apply');
1883
+
1884
+ return xNode('radioInput', {
1885
+ name,
1886
+ value,
1887
+ el: el.bindName()
1888
+ }, (ctx, n) => {
1889
+ ctx.write(true, `$runtime.radioButton(${n.el}, () => (${n.value}), () => (${n.name}), ($$) => {${n.name} = $$; $$apply();});`);
1890
+ });
1891
+ }
1892
+
1796
1893
  function buildRuntime() {
1797
1894
  this.module.head.push(xNode('$events', (ctx) => {
1798
1895
  if(this.inuse.$events) ctx.write(true, 'const $events = $option.events || {};');
@@ -1807,8 +1904,19 @@
1807
1904
  }
1808
1905
  }));
1809
1906
 
1810
- this.module.head.push(xNode('$context', (ctx) => {
1811
- if(this.inuse.$context) ctx.write(true, 'const $context = $option.context || {};');
1907
+ this.module.head.push(xNode('$context', {
1908
+ $hold: ['componentFn']
1909
+ }, (ctx) => {
1910
+ if(this.inuse.$context) {
1911
+ this.require('componentFn');
1912
+ ctx.write(true, 'const $context = $runtime.$context;');
1913
+ }
1914
+ }));
1915
+
1916
+ this.module.head.push(this.glob.keepAliveStore = xNode('$$keepAliveStore', {
1917
+ value: false
1918
+ }, (ctx, n) => {
1919
+ if(n.value) ctx.write(true, `const $$keepAliveStore = new Map();`);
1812
1920
  }));
1813
1921
 
1814
1922
  this.module.top.push(xNode(this.glob.$onMount, {
@@ -1851,7 +1959,6 @@
1851
1959
 
1852
1960
  let bb = this.buildBlock(this.DOM, {
1853
1961
  inline: true,
1854
- protectLastTag: true,
1855
1962
  allowSingleBlock: true,
1856
1963
  template: {
1857
1964
  name: '$parentElement',
@@ -1938,7 +2045,6 @@
1938
2045
  let binds = xNode('block');
1939
2046
  let result = {};
1940
2047
  let inuse = Object.assign({}, this.inuse);
1941
- let lastTagDynamic = false;
1942
2048
 
1943
2049
  if(!option.parentElement) option.parentElement = '$parentElement';
1944
2050
 
@@ -2021,26 +2127,51 @@
2021
2127
  if(svg && !other) rootSVG = true;
2022
2128
  }
2023
2129
 
2024
- let lastStatic;
2130
+ let labelRequest;
2025
2131
 
2026
- const placeLabel = name => {
2027
- let el;
2028
- if(lastStatic) {
2029
- el = lastStatic;
2030
- el.label = true;
2031
- lastStatic = null;
2032
- } else {
2033
- el = xNode('node:comment', { label: true, value: name });
2034
- tpl.push(el);
2132
+ const requireLabel = (final, noParent) => {
2133
+ if(labelRequest) {
2134
+ if(labelRequest.final) {
2135
+ labelRequest.set(tpl.push(xNode('node:comment', { label: true, value: '' })));
2136
+ } else {
2137
+ if(final) labelRequest.final = true;
2138
+ if(noParent) labelRequest.noParent = true;
2139
+ return labelRequest;
2140
+ }
2035
2141
  }
2036
- return el;
2142
+ labelRequest = {
2143
+ name: null,
2144
+ node: null,
2145
+ final,
2146
+ noParent,
2147
+ set(n) {
2148
+ labelRequest.name = n.bindName();
2149
+ labelRequest.node = n;
2150
+ labelRequest = null;
2151
+ },
2152
+ resolve() {
2153
+ assert(!labelRequest.node);
2154
+ if(labelRequest.noParent) {
2155
+ labelRequest.set(tpl.push(xNode('node:comment', { label: true, value: '' })));
2156
+ } else if(isRoot) {
2157
+ assert(!tpl._boundName);
2158
+ labelRequest.name = tpl._boundName = option.parentElement;
2159
+ } else {
2160
+ labelRequest.name = tpl.bindName();
2161
+ }
2162
+ labelRequest = null;
2163
+ }
2164
+ };
2165
+ return labelRequest;
2037
2166
  };
2038
2167
 
2039
2168
  const bindNode = (n, nodeIndex) => {
2040
2169
  if(n.type === 'text') {
2041
- if(isRoot) lastTagDynamic = false;
2042
2170
  let prev = tpl.getLast();
2043
- if(prev?.$type == 'node:text' && prev._boundName) tpl.push(xNode('node:comment', { label: true }));
2171
+ // if(prev?.$type == 'node:text' && prev._boundName) tpl.push(xNode('node:comment', { label: true }));
2172
+ if(prev?.$type == 'node:text' && labelRequest) {
2173
+ labelRequest.set(tpl.push(xNode('node:comment', { label: true })));
2174
+ }
2044
2175
 
2045
2176
  if(n.value.indexOf('{') >= 0) {
2046
2177
  const pe = this.parseText(n.value);
@@ -2074,57 +2205,57 @@
2074
2205
  }));
2075
2206
  });
2076
2207
 
2077
- lastStatic = textNode;
2208
+ labelRequest?.set(textNode);
2078
2209
  } else {
2079
- lastStatic = tpl.push(n.value);
2210
+ const textNode = tpl.push(n.value);
2211
+ labelRequest?.set(textNode);
2080
2212
  }
2213
+
2081
2214
  } else if(n.type === 'template') {
2082
- if(isRoot) lastTagDynamic = false;
2083
- lastStatic = null;
2084
- tpl.push(n.openTag);
2085
- tpl.push(n.content);
2086
- tpl.push('</template>');
2215
+ const templateNode = xNode('node', {
2216
+ openTag: n.openTag,
2217
+ content: n.content
2218
+ });
2219
+ templateNode.$handler = (ctx, n) => {
2220
+ ctx.write(n.openTag, n.content, '</template>');
2221
+ };
2222
+ tpl.push(templateNode);
2223
+ labelRequest?.set(templateNode);
2087
2224
  } else if(n.type === 'node') {
2088
2225
  if(n.name == 'malina' && !option.malinaElement) {
2089
2226
  let b;
2090
2227
  if(n.elArg == 'portal') b = this.attachPortal(n);
2091
- else b = this.attachHead(n);
2228
+ else if(['window', 'body', 'head'].includes(n.elArg)) b = this.attachHead(n);
2229
+ else if(n.elArg == 'self') {
2230
+ this.glob.$$selfComponent.$value();
2231
+ const label = requireLabel();
2232
+ let component = this.makeComponent(n, {self: true});
2233
+ binds.push(insertComponent(component, label));
2234
+ } else throw 'Wrong tag';
2092
2235
  b && binds.push(b);
2093
2236
  return;
2094
2237
  }
2095
- if(isRoot) lastTagDynamic = false;
2096
2238
  if(n.name == 'component' || n.name.match(/^[A-Z]/)) {
2097
2239
  if(n.name == 'component' || !n.elArg) {
2098
2240
  // component
2099
2241
  if(isRoot) requireFragment = true;
2100
- let el = placeLabel(n.name);
2101
2242
 
2102
2243
  if(n.name == 'component') {
2103
2244
  // dyn-component
2104
- binds.push(this.makeComponentDyn(n, el));
2245
+ if(isRoot) {
2246
+ requireFragment = true;
2247
+ if(!tpl.getLast()) tpl.push(xNode('node:comment', { label: true }));
2248
+ }
2249
+ const label = requireLabel(true, isRoot);
2250
+ binds.push(this.makeComponentDyn(n, label));
2105
2251
  } else {
2252
+ const label = requireLabel();
2106
2253
  let component = this.makeComponent(n);
2107
- binds.push(xNode('insert-component', {
2108
- component: component.bind,
2109
- reference: component.reference,
2110
- el: el.bindName()
2111
- }, (ctx, n) => {
2112
- if(n.reference) {
2113
- ctx.write(true, `${n.reference} = `);
2114
- ctx.add(n.component);
2115
- ctx.write(true, `$runtime.attachBlock(${n.el}, ${n.reference});`);
2116
- } else {
2117
- ctx.write(true, `$runtime.attachBlock(${n.el}, `);
2118
- ctx.add(n.component);
2119
- ctx.write(');');
2120
- }
2121
- }));
2254
+ binds.push(insertComponent(component, label));
2122
2255
  }
2123
2256
  } else {
2124
2257
  if(isRoot) requireFragment = true;
2125
- let el = placeLabel(`exported ${n.elArg}`);
2126
- let b = this.attchExportedFragment(n, el, n.name);
2127
- b && binds.push(b);
2258
+ binds.push(this.attchExportedFragment(n, requireLabel(), n.name));
2128
2259
  }
2129
2260
  return;
2130
2261
  }
@@ -2133,21 +2264,20 @@
2133
2264
  let slotName = n.elArg;
2134
2265
  if(!slotName) {
2135
2266
  if(option.context == 'fragment') {
2136
- let el = placeLabel('fragment-slot');
2137
- binds.push(this.attachFragmentSlot(el));
2267
+ binds.push(this.attachFragmentSlot(requireLabel()));
2138
2268
  return;
2139
2269
  } else slotName = 'default';
2140
2270
  }
2141
2271
 
2142
- let el = placeLabel(slotName);
2143
2272
  let slot = this.attachSlot(slotName, n);
2144
2273
 
2145
2274
  binds.push(xNode('attach-slot', {
2146
2275
  $compile: [slot],
2147
- el: el.bindName(),
2276
+ label: requireLabel(),
2148
2277
  slot
2149
2278
  }, (ctx, n) => {
2150
- ctx.write(true, `$runtime.attachBlock(${n.el}, `);
2279
+ if(n.label.node) ctx.write(true, `$runtime.insertBlock(${n.label.name}, `);
2280
+ else ctx.write(true, `$runtime.addBlock(${n.label.name}, `);
2151
2281
  ctx.add(n.slot);
2152
2282
  ctx.write(');', true);
2153
2283
  }));
@@ -2155,12 +2285,13 @@
2155
2285
  }
2156
2286
  if(n.name == 'fragment') {
2157
2287
  assert(n.elArg, 'Fragment name is required');
2158
- let el = placeLabel(`fragment ${n.elArg}`);
2288
+ if(isRoot) requireFragment = true;
2159
2289
  binds.push(xNode('attach-fragment', {
2160
- el: el.bindName(),
2290
+ label: requireLabel(),
2161
2291
  fragment: this.attachFragment(n)
2162
2292
  }, (ctx, n) => {
2163
- ctx.write(true, `$runtime.attachBlock(${n.el}, `);
2293
+ if(n.label.node) ctx.write(true, `$runtime.insertBlock(${n.label.name}, `);
2294
+ else ctx.write(true, `$runtime.addBlock(${n.label.name}, `);
2164
2295
  ctx.add(n.fragment);
2165
2296
  ctx.write(')');
2166
2297
  }));
@@ -2170,7 +2301,7 @@
2170
2301
  let el = xNode('node', { name: n.name });
2171
2302
  if(option.oneElement) el._boundName = option.oneElement;
2172
2303
  tpl.push(el);
2173
- lastStatic = el;
2304
+ labelRequest?.set(el);
2174
2305
 
2175
2306
  if(n.attributes.some(a => a.name.startsWith('{...'))) {
2176
2307
  this.require('rootCD');
@@ -2182,8 +2313,15 @@
2182
2313
  ctx.writeLine(`$runtime.spreadAttributes(${n.el}, () => ({${n.props.join(', ')}}));`);
2183
2314
  }));
2184
2315
  }
2316
+
2317
+ if(n.name == 'input') {
2318
+ const b = radioInput.call(this, n, el);
2319
+ b && binds.push(b);
2320
+ }
2321
+
2185
2322
  let bindTail = [];
2186
2323
  n.attributes.forEach(p => {
2324
+ if(p._skip) return;
2187
2325
  let b = this.bindProp(p, n, el);
2188
2326
  if(b) {
2189
2327
  if(b.bind) binds.push(b.bind);
@@ -2215,11 +2353,24 @@
2215
2353
  if(!n.closedTag) {
2216
2354
  go(n, false, el);
2217
2355
  }
2356
+ } else if(n.type === 'block') {
2357
+ if(n.name == 'keep') {
2358
+ if(isRoot) requireFragment = true;
2359
+ binds.push(xNode('attach-fragment', {
2360
+ label: requireLabel(),
2361
+ block: this.makeKeepAlive(n)
2362
+ }, (ctx, n) => {
2363
+ if(n.label.node) ctx.write(true, `$runtime.insertBlock(${n.label.name}, `);
2364
+ else ctx.write(true, `$runtime.addBlock(${n.label.name}, `);
2365
+ ctx.add(n.block);
2366
+ ctx.write(')');
2367
+ }));
2368
+ return;
2369
+ } else wrapException(`wrong block: "${n.name}"`, n);
2218
2370
  } else if(n.type === 'each') {
2219
2371
  if(data.type == 'node' && data.body.length == 1) {
2220
- lastStatic = null;
2221
2372
  let eachBlock = this.makeEachBlock(n, {
2222
- elName: tpl.bindName(),
2373
+ label: tpl.bindName(),
2223
2374
  onlyChild: true
2224
2375
  });
2225
2376
  binds.push(eachBlock.source);
@@ -2227,20 +2378,18 @@
2227
2378
  } else {
2228
2379
  if(isRoot) {
2229
2380
  requireFragment = true;
2230
- lastTagDynamic = true;
2381
+ if(!tpl.getLast()) tpl.push(xNode('node:comment', { label: true }));
2231
2382
  }
2232
- let element = placeLabel(n.value);
2233
- let eachBlock = this.makeEachBlock(n, { elName: element.bindName() });
2383
+ let eachBlock = this.makeEachBlock(n, { label: requireLabel(true, isRoot) });
2234
2384
  binds.push(eachBlock.source);
2235
2385
  return;
2236
2386
  }
2237
2387
  } else if(n.type === 'if') {
2238
2388
  if(isRoot) {
2239
2389
  requireFragment = true;
2240
- lastTagDynamic = true;
2390
+ if(!tpl.getLast()) tpl.push(xNode('node:comment', { label: true }));
2241
2391
  }
2242
- if(nodeIndex == 0 && !isRoot) binds.push(this.makeifBlock(n, tpl, true));
2243
- else binds.push(this.makeifBlock(n, placeLabel(n.value)));
2392
+ binds.push(this.makeifBlock(n, requireLabel(true, isRoot)));
2244
2393
  return;
2245
2394
  } else if(n.type === 'systag') {
2246
2395
  let r = n.value.match(/^@(\w+)\s+(.*)$/s);
@@ -2250,24 +2399,21 @@
2250
2399
  if(name == 'html') {
2251
2400
  if(isRoot) {
2252
2401
  requireFragment = true;
2253
- lastTagDynamic = true;
2402
+ if(!tpl.getLast()) tpl.push(xNode('node:comment', { label: true }));
2254
2403
  }
2255
- let el = placeLabel('html');
2256
- binds.push(this.makeHtmlBlock(exp, el));
2404
+ binds.push(this.makeHtmlBlock(exp, requireLabel(true, true)));
2257
2405
  return;
2258
2406
  } else throw 'Wrong tag';
2259
2407
  } else if(n.type === 'await') {
2260
2408
  if(isRoot) {
2261
2409
  requireFragment = true;
2262
- lastTagDynamic = true;
2410
+ if(!tpl.getLast()) tpl.push(xNode('node:comment', { label: true }));
2263
2411
  }
2264
- let el = placeLabel(n.value);
2265
- let r = this.makeAwaitBlock(n, el);
2266
- r && binds.push(r);
2412
+ binds.push(this.makeAwaitBlock(n, requireLabel(true, isRoot)));
2267
2413
  return;
2268
2414
  } else if(n.type === 'comment') {
2269
- if(isRoot) lastTagDynamic = false;
2270
- lastStatic = tpl.push(n.content);
2415
+ const commentNode = tpl.push(n.content);
2416
+ labelRequest?.set(commentNode);
2271
2417
  }
2272
2418
  };
2273
2419
  body.forEach((node, i) => {
@@ -2277,11 +2423,10 @@
2277
2423
  wrapException(e, node);
2278
2424
  }
2279
2425
  });
2426
+ labelRequest?.resolve();
2280
2427
  };
2428
+
2281
2429
  go(data, true, rootTemplate);
2282
- if(option.protectLastTag && lastTagDynamic) {
2283
- rootTemplate.push(xNode('node:comment', { value: '' }));
2284
- }
2285
2430
 
2286
2431
  let innerBlock = null;
2287
2432
  if(binds.body.length) {
@@ -2528,12 +2673,32 @@
2528
2673
  console.log('Node: ', n);
2529
2674
  if(n.type == 'text') e.details = n.value.trim();
2530
2675
  else if(n.type == 'node') e.details = n.openTag.trim();
2531
- else if(n.type == 'each') e.details = n.value.trim();
2532
- else if(n.type == 'if') e.details = n.value.trim();
2676
+ else if(n.type == 'each' || n.type == 'block') e.details = n.value.trim();
2677
+ else if(n.type == 'if') e.details = n.parts?.[0]?.value.trim() || 'if-block';
2533
2678
  }
2534
2679
  throw e;
2535
2680
  }
2536
2681
 
2682
+ function insertComponent(component, label) {
2683
+ return xNode('insert-component', {
2684
+ component: component.bind,
2685
+ reference: component.reference,
2686
+ label
2687
+ }, (ctx, n) => {
2688
+ if(n.reference) {
2689
+ ctx.write(true, `${n.reference} = `);
2690
+ ctx.add(n.component);
2691
+ if(n.label.node) ctx.write(true, `$runtime.insertBlock(${n.label.name}, ${n.reference});`);
2692
+ else ctx.write(true, `$runtime.addBlock(${n.label.name}, ${n.reference});`);
2693
+ } else {
2694
+ if(n.label.node) ctx.write(true, `$runtime.insertBlock(${n.label.name}, `);
2695
+ else ctx.write(true, `$runtime.addBlock(${n.label.name}, `);
2696
+ ctx.add(n.component);
2697
+ ctx.write(');');
2698
+ }
2699
+ })
2700
+ }
2701
+
2537
2702
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
2538
2703
 
2539
2704
  function createCommonjsModule(fn, basedir, module) {
@@ -4655,7 +4820,7 @@
4655
4820
  function makeDom(data) {
4656
4821
  function build(parent, list) {
4657
4822
  list.forEach(e => {
4658
- if(e.type == 'fragment' || e.type == 'slot') {
4823
+ if(e.type == 'fragment' || e.type == 'slot' || e.type == 'block') {
4659
4824
  if(e.body && e.body.length) build(parent, e.body);
4660
4825
  return;
4661
4826
  } else if(e.type == 'each') {
@@ -4772,7 +4937,7 @@
4772
4937
  return result;
4773
4938
  };
4774
4939
 
4775
- function makeComponent(node) {
4940
+ function makeComponent(node, option={}) {
4776
4941
  let propList = node.attributes;
4777
4942
 
4778
4943
  this.require('$context');
@@ -4782,8 +4947,8 @@
4782
4947
  let slotBlocks = [];
4783
4948
  let anchorBlocks = [];
4784
4949
 
4785
- let componentName = node.name;
4786
- if(componentName != 'component' && this.config.autoimport) {
4950
+ let componentName = option.self ? '$$selfComponent' : node.name;
4951
+ if(componentName != 'component' && this.config.autoimport && !option.self) {
4787
4952
  let imported = this.script.autoimport[componentName] || this.script.importedNames.includes(componentName) ||
4788
4953
  this.script.rootVariables[componentName] || this.script.rootFunctions[componentName];
4789
4954
 
@@ -5128,7 +5293,7 @@
5128
5293
  return { bind: result, reference };
5129
5294
  }
5130
5295
 
5131
- function makeComponentDyn(node, element) {
5296
+ function makeComponentDyn(node, label) {
5132
5297
  let dynamicComponent;
5133
5298
 
5134
5299
  if(node.elArg) {
@@ -5150,15 +5315,16 @@
5150
5315
 
5151
5316
  component.componentName = '$ComponentConstructor';
5152
5317
  return xNode('dyn-component', {
5153
- el: element.bindName(),
5318
+ label,
5154
5319
  exp: dynamicComponent,
5155
5320
  component,
5156
5321
  reference
5157
5322
  }, (ctx, n) => {
5158
- ctx.write(true, `$runtime.attachDynComponent(${n.el}, () => ${n.exp}, ($ComponentConstructor) => `);
5323
+ ctx.write(true, `$runtime.attachDynComponent(${n.label.name}, () => ${n.exp}, ($ComponentConstructor) => `);
5159
5324
  if(n.reference) ctx.write(`${n.reference} = `);
5160
5325
  ctx.add(n.component);
5161
- ctx.write(')');
5326
+ if(n.label.node) ctx.write(')');
5327
+ else ctx.write(', true)');
5162
5328
  });
5163
5329
  }
5164
5330
 
@@ -5279,7 +5445,7 @@
5279
5445
  ctx.write(');');
5280
5446
  })
5281
5447
  };
5282
- } else if(name == 'bind') {
5448
+ } else if(name == 'bind' && arg) {
5283
5449
  if(this.script.readOnly) {
5284
5450
  this.warning('script read-only conflicts with bind: ' + node.openTag);
5285
5451
  return;
@@ -5307,22 +5473,38 @@
5307
5473
  }
5308
5474
 
5309
5475
  assert(['value', 'checked', 'valueAsNumber', 'valueAsDate', 'selectedIndex'].includes(attr), 'Not supported: ' + prop.content);
5310
- assert(arg.length == 0);
5311
5476
  assert(detectExpressionType(exp) == 'identifier', 'Wrong bind name: ' + prop.content);
5312
- if(attr == 'value' && ['number', 'range'].includes(inputType)) attr = 'valueAsNumber';
5477
+ assert(arg.length == 0);
5313
5478
  this.detectDependency(exp);
5479
+ let argName = '$$a' + (this.uniqIndex++);
5314
5480
 
5315
- let argName = 'a' + (this.uniqIndex++);
5481
+ if(node.name == 'select' && attr == 'value') {
5482
+ return {
5483
+ bind: xNode('bindInput', {
5484
+ el: element.bindName(),
5485
+ exp,
5486
+ attr,
5487
+ argName
5488
+ }, (ctx, n) => {
5489
+ ctx.write(true, `$runtime.selectElement(${n.el}, () => ${n.exp}, ${n.argName} => {${n.exp} = ${n.argName}; $$apply();});`);
5490
+ })
5491
+ }
5492
+ }
5493
+
5494
+ if(attr == 'value' && ['number', 'range'].includes(inputType)) attr = 'valueAsNumber';
5316
5495
 
5317
5496
  return {
5318
5497
  bind: xNode('bindInput', {
5319
- el: element.bindName()
5498
+ el: element.bindName(),
5499
+ exp,
5500
+ attr,
5501
+ argName
5320
5502
  }, (ctx, n) => {
5321
- ctx.writeLine(`$runtime.bindInput(${n.el}, '${attr}', () => ${exp}, ${argName} => {${exp} = ${argName}; $$apply();});`);
5503
+ ctx.write(true, `$runtime.bindInput(${n.el}, '${n.attr}', () => ${n.exp}, ${n.argName} => {${n.exp} = ${n.argName}; $$apply();});`);
5322
5504
  })
5323
5505
  };
5324
5506
  } else if(name == 'style' && arg) {
5325
- let styleName = toCamelCase(arg);
5507
+ let styleName = arg;
5326
5508
  let exp;
5327
5509
  if(prop.value) {
5328
5510
  if(isExpression(prop.value)) {
@@ -5340,13 +5522,13 @@
5340
5522
  name: styleName,
5341
5523
  value: prop.value
5342
5524
  }, (ctx, n) => {
5343
- ctx.writeLine(`${n.el}.style.${n.name} = \`${Q(n.value)}\`;`);
5525
+ ctx.writeLine(`${n.el}.style.${toCamelCase(n.name)} = \`${Q(n.value)}\`;`);
5344
5526
  })
5345
5527
  };
5346
5528
  }
5347
5529
  }
5348
5530
  } else {
5349
- exp = styleName;
5531
+ exp = toCamelCase(styleName);
5350
5532
  }
5351
5533
 
5352
5534
  let hasElement = exp.includes('$element');
@@ -5363,7 +5545,7 @@
5363
5545
  if(ctx.inuse.apply) {
5364
5546
  ctx.writeLine(`$runtime.bindStyle(${n.el}, '${n.styleName}', () => (${n.exp}));`);
5365
5547
  } else {
5366
- ctx.writeLine(`${n.el}.style.${n.styleName} = ${n.exp};`);
5548
+ ctx.writeLine(`${n.el}.style.${toCamelCase(n.styleName)} = ${n.exp};`);
5367
5549
  }
5368
5550
  })]
5369
5551
  })
@@ -5550,6 +5732,17 @@
5550
5732
 
5551
5733
  if(node.spreading) return node.spreading.push(`${name}: ${exp}`);
5552
5734
 
5735
+ if(node.name == 'option' && name == 'value' && parsed.binding) {
5736
+ return {
5737
+ bind: xNode('bindOptionValue', {
5738
+ el: element.bindName(),
5739
+ value: parsed.binding
5740
+ }, (ctx, n) => {
5741
+ ctx.write(true, `$runtime.selectOption(${n.el}, () => (${n.value}));`);
5742
+ })
5743
+ }
5744
+ }
5745
+
5553
5746
  const propList = {
5554
5747
  hidden: true,
5555
5748
  checked: true,
@@ -5558,6 +5751,7 @@
5558
5751
  selected: true,
5559
5752
  innerHTML: true,
5560
5753
  innerText: true,
5754
+ multiple: node.name == 'select',
5561
5755
  src: true,
5562
5756
  readonly: 'readOnly'
5563
5757
  };
@@ -5565,7 +5759,7 @@
5565
5759
  let n = xNode('bindAttribute', {
5566
5760
  $wait: ['apply'],
5567
5761
  name,
5568
- exp,
5762
+ exp: propList[name] && parsed.binding ? parsed.binding : exp,
5569
5763
  hasElement,
5570
5764
  el: element.bindName()
5571
5765
  }, (ctx, data) => {
@@ -5603,7 +5797,7 @@
5603
5797
  }
5604
5798
  }
5605
5799
 
5606
- function makeifBlock(data, element, parentElement) {
5800
+ function makeifBlock(data, label) {
5607
5801
  const getBlock = b => {
5608
5802
  if(b.singleBlock) {
5609
5803
  return xNode('make-block', {
@@ -5624,22 +5818,21 @@
5624
5818
  this.detectDependency(exp);
5625
5819
  parts.push({
5626
5820
  exp,
5627
- block: getBlock(this.buildBlock(part, { protectLastTag: true, allowSingleBlock: true }))
5821
+ block: getBlock(this.buildBlock(part, { allowSingleBlock: true }))
5628
5822
  });
5629
5823
  });
5630
- if(data.elsePart) elseBlock = getBlock(this.buildBlock({ body: data.elsePart }, { protectLastTag: true, allowSingleBlock: true }));
5824
+ if(data.elsePart) elseBlock = getBlock(this.buildBlock({ body: data.elsePart }, { allowSingleBlock: true }));
5631
5825
 
5632
5826
  return xNode('if:bind', {
5633
5827
  $wait: ['apply'],
5634
- el: element.bindName(),
5635
- parentElement,
5828
+ label,
5636
5829
  parts,
5637
5830
  elseBlock
5638
5831
  }, (ctx, n) => {
5639
5832
  if(this.inuse.apply) {
5640
- ctx.write(true, `$runtime.ifBlock(${n.el}, `);
5833
+ ctx.write(true, `$runtime.ifBlock(${n.label.name}, `);
5641
5834
  } else {
5642
- ctx.write(true, `$runtime.ifBlockReadOnly(${n.el}, `);
5835
+ ctx.write(true, `$runtime.ifBlockReadOnly(${n.label.name}, `);
5643
5836
  }
5644
5837
 
5645
5838
  if(n.parts.length == 1) {
@@ -5665,7 +5858,7 @@
5665
5858
 
5666
5859
  ctx.indent--;
5667
5860
  ctx.write(true);
5668
- if(n.parentElement) ctx.write(', true');
5861
+ if(!n.label.node) ctx.write(', true');
5669
5862
  ctx.write(');', true);
5670
5863
  });
5671
5864
  }
@@ -5783,7 +5976,6 @@
5783
5976
  if(!nodeItems.length) nodeItems = [data.mainBlock[0]];
5784
5977
 
5785
5978
  let itemBlock, block = this.buildBlock({ body: nodeItems }, {
5786
- protectLastTag: true,
5787
5979
  allowSingleBlock: !blockPrefix,
5788
5980
  each: {
5789
5981
  blockPrefix,
@@ -5817,7 +6009,6 @@
5817
6009
  let elseBlock = null;
5818
6010
  if(data.elseBlock) {
5819
6011
  let block = this.buildBlock({ body: data.elseBlock }, {
5820
- protectLastTag: true,
5821
6012
  allowSingleBlock: false
5822
6013
  });
5823
6014
  elseBlock = block.block;
@@ -5826,9 +6017,15 @@
5826
6017
  const source = xNode('each', {
5827
6018
  keyFunction,
5828
6019
  block: itemBlock,
5829
- elseBlock
6020
+ elseBlock,
6021
+ label: option.label,
6022
+ onlyChild: option.onlyChild
5830
6023
  }, (ctx, n) => {
5831
- ctx.writeLine(`$runtime.$$eachBlock(${option.elName}, ${option.onlyChild ? 1 : 0}, () => (${arrayName}),`);
6024
+ let el = n.onlyChild ? n.label : n.label.name;
6025
+ let mode = 0;
6026
+ if(n.onlyChild) mode = 1;
6027
+ else if(!n.label.node) mode = 2;
6028
+ ctx.writeLine(`$runtime.$$eachBlock(${el}, ${mode}, () => (${arrayName}),`);
5832
6029
  ctx.indent++;
5833
6030
  ctx.write(true);
5834
6031
  if(n.keyFunction === 'noop') ctx.write('$runtime.noop');
@@ -5853,15 +6050,15 @@
5853
6050
  this.detectDependency(exp);
5854
6051
  return xNode('block', {
5855
6052
  $wait: ['apply'],
5856
- el: label.bindName(),
6053
+ label,
5857
6054
  exp
5858
6055
  }, (ctx, n) => {
5859
- if(this.inuse.apply) ctx.write(true, `$runtime.htmlBlock(${n.el}, () => (${n.exp}));`);
5860
- else ctx.write(true, `$runtime.htmlBlockStatic(${n.el}, ${n.exp});`);
6056
+ if(this.inuse.apply) ctx.write(true, `$runtime.htmlBlock(${n.label.name}, () => (${n.exp}));`);
6057
+ else ctx.write(true, `$runtime.htmlBlockStatic(${n.label.name}, ${n.exp});`);
5861
6058
  });
5862
6059
  }
5863
6060
 
5864
- function makeAwaitBlock(node, element) {
6061
+ function makeAwaitBlock(node, label) {
5865
6062
  let valueForThen, exp;
5866
6063
 
5867
6064
  let rx = node.value.match(/^#await\s+(.+)\s+then\s+(\S+)\s*$/s);
@@ -5881,7 +6078,7 @@
5881
6078
 
5882
6079
  let parts = [null, null, null];
5883
6080
  if(node.parts.main && node.parts.main.length) {
5884
- parts[0] = this.buildBlock({ body: node.parts.main }, { protectLastTag: true });
6081
+ parts[0] = this.buildBlock({ body: node.parts.main });
5885
6082
  }
5886
6083
  if(node.parts.then && node.parts.then.length) {
5887
6084
  let args = [];
@@ -5895,7 +6092,7 @@
5895
6092
  args.push(rx[1]);
5896
6093
  }
5897
6094
  }
5898
- parts[1] = this.buildBlock({ body: node.parts.then }, { protectLastTag: true, extraArguments: args });
6095
+ parts[1] = this.buildBlock({ body: node.parts.then }, { extraArguments: args });
5899
6096
  }
5900
6097
  if(node.parts.catch && node.parts.catch.length) {
5901
6098
  let args = [];
@@ -5904,7 +6101,7 @@
5904
6101
  assert(isSimpleName(rx[1]));
5905
6102
  args.push(rx[1]);
5906
6103
  }
5907
- parts[2] = this.buildBlock({ body: node.parts.catch }, { protectLastTag: true, extraArguments: args });
6104
+ parts[2] = this.buildBlock({ body: node.parts.catch }, { extraArguments: args });
5908
6105
  }
5909
6106
 
5910
6107
  if(this.script.readOnly) {
@@ -5915,12 +6112,12 @@
5915
6112
  this.require('apply');
5916
6113
 
5917
6114
  return xNode('await', {
5918
- el: element.bindName(),
6115
+ label,
5919
6116
  exp,
5920
6117
  parts,
5921
6118
  keywords
5922
6119
  }, (ctx, n) => {
5923
- ctx.write(true, `$runtime.awaitBlock(${n.el}, () => [${n.keywords.join(', ')}], () => ${n.exp},`);
6120
+ ctx.write(true, `$runtime.awaitBlock(${n.label.name}, ${n.label.node ? 0 : 1}, () => [${n.keywords.join(', ')}], () => ${n.exp},`);
5924
6121
  ctx.indent++;
5925
6122
  n.parts.forEach((part, index) => {
5926
6123
  if(index) ctx.write(', ');
@@ -6070,7 +6267,7 @@
6070
6267
  }
6071
6268
 
6072
6269
 
6073
- function parseAttibutes(attributes) {
6270
+ function parseAttibutes$1(attributes) {
6074
6271
  let props = [];
6075
6272
  let events = [];
6076
6273
  let forwardAllEvents;
@@ -6115,7 +6312,7 @@
6115
6312
  let slot = null;
6116
6313
  if(node.body?.length) slot = this.buildBlock({ body: trimEmptyNodes(node.body) }, { inline: true });
6117
6314
 
6118
- let { props, events, forwardAllEvents, staticProps } = parseAttibutes.call(this, node.attributes);
6315
+ let { props, events, forwardAllEvents, staticProps } = parseAttibutes$1.call(this, node.attributes);
6119
6316
 
6120
6317
  return xNode('call-fragment', {
6121
6318
  $compile: [slot?.source],
@@ -6192,9 +6389,10 @@
6192
6389
 
6193
6390
  function attachFragmentSlot(label) {
6194
6391
  return xNode('fragment-slot', {
6195
- el: label.bindName()
6392
+ label
6196
6393
  }, (ctx, n) => {
6197
- ctx.write(true, `$runtime.attachBlock(${n.el}, $$fragmentSlot?.())`);
6394
+ if(n.label.node) ctx.write(true, `$runtime.insertBlock(${n.label.name}, $$fragmentSlot?.())`);
6395
+ else ctx.write(true, `$runtime.addBlock(${n.label.name}, $$fragmentSlot?.())`);
6198
6396
  });
6199
6397
  }
6200
6398
 
@@ -6203,7 +6401,7 @@
6203
6401
  let data = {
6204
6402
  name: node.elArg,
6205
6403
  componentName,
6206
- label: label.bindName()
6404
+ label
6207
6405
  };
6208
6406
 
6209
6407
  let body = trimEmptyNodes(node.body || []);
@@ -6214,11 +6412,12 @@
6214
6412
  // assert(!data.slot.template.svg, 'SVG is not supported for exported fragment');
6215
6413
  }
6216
6414
 
6217
- let pa = parseAttibutes.call(this, node.attributes);
6415
+ let pa = parseAttibutes$1.call(this, node.attributes);
6218
6416
  data = { ...pa, ...data };
6219
6417
 
6220
6418
  return xNode('attach-exported-fragment', data, (ctx, n) => {
6221
- ctx.write(true, `$runtime.attachBlock(${n.label}, $runtime.callExportedFragment($instance_${n.componentName}, '${n.name}'`);
6419
+ if(n.label.node) ctx.write(true, `$runtime.insertBlock(${n.label.name}, $runtime.callExportedFragment($instance_${n.componentName}, '${n.name}'`);
6420
+ else ctx.write(true, `$runtime.addBlock(${n.label.name}, $runtime.callExportedFragment($instance_${n.componentName}, '${n.name}'`);
6222
6421
  ctx.indent++;
6223
6422
  let missed = '';
6224
6423
 
@@ -6597,7 +6796,44 @@
6597
6796
  return { event, fn, rootModifier };
6598
6797
  }
6599
6798
 
6600
- const version = '0.7.2-a1';
6799
+ function makeKeepAlive(node) {
6800
+ let block;
6801
+ if(node.body && node.body.length) {
6802
+ block = this.buildBlock({ body: trimEmptyNodes(node.body) }, { }).block;
6803
+ } else {
6804
+ this.warning(`Empty block: '${node.value}'`);
6805
+ return xNode('empty-block', (ctx, n) => {
6806
+ ctx.writeLine(`function $block() {};`);
6807
+ });
6808
+ }
6809
+
6810
+ let key = null;
6811
+ let args = node.value.substr(6);
6812
+ if(args) {
6813
+ args = parseAttibutes(args);
6814
+ const a = args.find(a => a.name == 'key');
6815
+ if(a) {
6816
+ let value = a.value;
6817
+ if(value[0] == '{') value = unwrapExp(value);
6818
+ key = `() => (${value})`;
6819
+ }
6820
+ }
6821
+
6822
+ if(!key) key = `() => '$$${this.uniqIndex++}'`;
6823
+
6824
+ this.glob.keepAliveStore.$value();
6825
+
6826
+ return xNode('keep-alive', {
6827
+ block,
6828
+ key
6829
+ }, (ctx, n) => {
6830
+ ctx.write(`$runtime.keepAlive($$keepAliveStore, ${n.key}, `);
6831
+ ctx.add(n.block);
6832
+ ctx.write(')');
6833
+ });
6834
+ }
6835
+
6836
+ const version = '0.7.2-a11';
6601
6837
 
6602
6838
 
6603
6839
  async function compile(source, config = {}) {
@@ -6643,6 +6879,7 @@
6643
6879
  inspectProp,
6644
6880
  attachPortal,
6645
6881
  makeEventProp,
6882
+ makeKeepAlive,
6646
6883
  checkRootName: checkRootName,
6647
6884
 
6648
6885
  inuse: {},
@@ -6651,7 +6888,8 @@
6651
6888
  rootCD: xNode('root-cd', false),
6652
6889
  apply: xNode('apply', false),
6653
6890
  componentFn: xNode('componentFn', false),
6654
- $onMount: xNode('$onMount', false)
6891
+ $onMount: xNode('$onMount', false),
6892
+ $$selfComponent: xNode('$$selfComponent', false)
6655
6893
  },
6656
6894
  require: function(...args) {
6657
6895
  for(let name of args) {
@@ -6672,12 +6910,14 @@
6672
6910
  detectDependency,
6673
6911
 
6674
6912
  DOM: null,
6675
- parseHTML: parse,
6913
+ parseHTML: function() {
6914
+ this.DOM = parseHTML(this.source);
6915
+ },
6676
6916
  compactDOM,
6677
6917
 
6678
6918
  script: null,
6679
6919
  scriptNodes: null,
6680
- js_parse: parse$1,
6920
+ js_parse: parse,
6681
6921
  js_transform: transform,
6682
6922
 
6683
6923
  styleNodes: null,
@@ -6746,10 +6986,21 @@
6746
6986
  $compile: [ctx.module.head, ctx.module.code, ctx.module.body, ctx.glob.rootCD],
6747
6987
  name: config.name,
6748
6988
  componentFn: ctx.glob.componentFn
6749
- }, (ctx, n) => {
6750
- if(config.exportDefault) ctx.write(true, 'export default ');
6751
- else ctx.write(true, `const ${n.name} = `);
6752
- ctx.add(n.componentFn);
6989
+ }, (ctx2, n) => {
6990
+ if(config.exportDefault) {
6991
+ if(ctx.glob.$$selfComponent.value) {
6992
+ ctx2.write(true, 'const $$selfComponent = ');
6993
+ ctx2.add(n.componentFn);
6994
+ ctx2.write(true, 'export default $$selfComponent;');
6995
+ } else {
6996
+ ctx2.write(true, 'export default ');
6997
+ ctx2.add(n.componentFn);
6998
+ }
6999
+ } else {
7000
+ assert(!ctx.glob.$$selfComponent.value, 'Not supported');
7001
+ ctx2.write(true, `const ${n.name} = `);
7002
+ ctx2.add(n.componentFn);
7003
+ }
6753
7004
  }));
6754
7005
 
6755
7006
  ctx.result = xBuild(result);
@@ -6837,6 +7088,7 @@
6837
7088
  }
6838
7089
 
6839
7090
  exports.compile = compile;
7091
+ exports.parseHTML = parseHTML;
6840
7092
  exports.version = version;
6841
7093
 
6842
7094
  Object.defineProperty(exports, '__esModule', { value: true });