malinajs 0.6.50 → 0.7.1-alpha

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
@@ -25,10 +25,10 @@
25
25
  });
26
26
  }
27
27
  function Q(s) {
28
- return s.replace(/\\/g, '\\\\').replace(/`/g, '\\`');
28
+ return s.replace(/`/g, '\\`').replace(/\\/g, '\\\\');
29
29
  }
30
30
  function Q2(s) {
31
- return s.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\n/g, '\\n');
31
+ return s.replace(/`/g, '\\`').replace(/\\/g, '\\\\').replace(/\n/g, '\\n');
32
32
  }
33
33
  function unwrapExp(e) {
34
34
  assert(e, 'Empty expression');
@@ -79,11 +79,20 @@
79
79
  return true;
80
80
  }
81
81
 
82
+ function checkFunctionCall(body) {
83
+ if(body.length != 1) return;
84
+ if(body[0].type != 'ExpressionStatement') return;
85
+ let obj = body[0].expression;
86
+ if(obj.type != 'CallExpression') return;
87
+ if(obj.callee?.type == 'Identifier') return obj.callee.name;
88
+ }
89
+
82
90
  if(checkIdentificator(ast.body)) return 'identifier';
83
91
  if(checkMemberIdentificator(ast.body)) return 'identifier';
84
92
  if(checkFunction(ast.body)) return 'function';
85
93
 
86
- return;
94
+ let fn = checkFunctionCall(ast.body);
95
+ if(fn) return {type: 'function-call', name: fn};
87
96
  }
88
97
 
89
98
  function checkRootName(name) {
@@ -111,147 +120,6 @@
111
120
  }
112
121
 
113
122
 
114
- function compactDOM() {
115
- let data = this.DOM;
116
- const details = {
117
- node: [n => n.body],
118
- each: [n => n.body],
119
- slot: [n => n.body],
120
- fragment: [n => n.body],
121
- if: [n => n.body, n => n.bodyMain],
122
- await: [n => n.parts.main, n => n.parts.then, n => n.parts.catch]
123
- };
124
-
125
- function go(body, parentNode) {
126
- let i;
127
-
128
- const getPrev = () => {
129
- return i > 0 && body.length ? body[i - 1] : null;
130
- };
131
-
132
- const getNext = () => {
133
- return i < body.length ? body[i + 1] : null;
134
- };
135
-
136
- for(i=0; i<body.length; i++) {
137
- let node = body[i];
138
- if(node.type == 'text') {
139
- let next = getNext();
140
- if(next && next.type == 'text') {
141
- node.value += next.value;
142
- body.splice(i + 1, 1);
143
- }
144
-
145
- if(node.value) {
146
- if(!node.value.trim()) {
147
- node.value = ' ';
148
- } else {
149
- let rx = node.value.match(/^(\s*)(.*?)(\s*)$/);
150
- if(rx) {
151
- let r = '';
152
- if(rx[1]) r += ' ';
153
- r += rx[2];
154
- if(rx[3]) r += ' ';
155
- node.value = r;
156
- }
157
- }
158
- }
159
- } else {
160
- if(node.type == 'node' && (node.name == 'pre' || node.name == 'textarea')) continue;
161
- let keys = details[node.type];
162
- keys && keys.forEach(k => {
163
- let body = k(node);
164
- if(body && body.length) go(body, node);
165
- });
166
- }
167
- }
168
-
169
- const isTable = n => ['thead', 'tbody', 'tfoot', 'tr', 'td', 'th', 'colgroup', 'col'].includes(n.name);
170
-
171
- i = 0;
172
- while(i < body.length) {
173
- let node = body[i];
174
- if(node.type == 'text' && !node.value.trim()) {
175
- if(parentNode && (parentNode.name == 'table' || isTable(parentNode)) && (i == 0 || i == body.length -1)) {
176
- body.splice(i, 1);
177
- continue;
178
- }
179
-
180
- let prev = getPrev();
181
- let next = getNext();
182
- if(prev && next) {
183
- if(prev.type == 'node' && next.type == 'node') {
184
- if(isTable(prev) && isTable(next) ||
185
- prev.name == 'li' && next.name == 'li' ||
186
- prev.name == 'div' && next.name == 'div') {
187
- body.splice(i, 1);
188
- continue;
189
- }
190
- }
191
- } else if(parentNode) {
192
- let p = prev && prev.type == 'node' && prev.name;
193
- let n = next && next.type == 'node' && next.name;
194
-
195
- if((p == 'td' || n == 'td') && ((parentNode.type == 'node' && parentNode.name == 'tr') || (parentNode.type == 'each'))) {
196
- body.splice(i, 1);
197
- continue;
198
- }
199
- if((p == 'tbody' || n == 'tbody') && (parentNode.type == 'node' && parentNode.name == 'table')) {
200
- body.splice(i, 1);
201
- continue;
202
- }
203
- if((p == 'li' || n == 'li') && (parentNode.type == 'node' && parentNode.name == 'ul')) {
204
- body.splice(i, 1);
205
- continue;
206
- }
207
- if(parentNode.type == 'node' && parentNode.name == 'div') {
208
- body.splice(i, 1);
209
- continue;
210
- }
211
- if(parentNode.type == 'node' && (prev && prev.type == 'each' || next && next.type == 'each')) {
212
- body.splice(i, 1);
213
- continue;
214
- }
215
- }
216
- }
217
- i++;
218
- }
219
-
220
- }
221
-
222
- function trimNodes(srcNodes) {
223
- let nodes = srcNodes.slice();
224
- let ex = [];
225
- while(nodes.length) {
226
- let n = nodes[0];
227
- if(n.type == 'fragment' || n.type == 'comment') {
228
- ex.push(n);
229
- nodes.shift();
230
- continue;
231
- }
232
- if(n.type == 'text' && !n.value.trim()) nodes.shift();
233
- else break;
234
- }
235
- nodes = [...ex, ...nodes];
236
- ex = [];
237
- while(nodes.length) {
238
- let n = last(nodes);
239
- if(n.type == 'fragment' || n.type == 'comment') {
240
- ex.push(n);
241
- nodes.pop();
242
- continue;
243
- }
244
- if(n.type == 'text' && !n.value.trim()) nodes.pop();
245
- else break;
246
- }
247
- return [...nodes, ...ex];
248
- }
249
-
250
- data.body = trimNodes(data.body);
251
-
252
- go(data.body);
253
- }
254
-
255
123
  const genId = () => {
256
124
  let id = Math.floor(Date.now() * Math.random()).toString(36);
257
125
  if(id.length > 6) id = id.substring(id.length - 6);
@@ -347,95 +215,136 @@
347
215
  return result;
348
216
  };
349
217
 
350
-
351
218
  function I(value = 0) {
352
219
  this.$indent = value;
353
220
  }
354
221
 
355
- function xWriter(ctx) {
222
+
223
+ function xWriter(ctx, node) {
356
224
  this._ctx = ctx;
357
225
  this.inuse = ctx.inuse;
358
- this.result = [];
359
- this.indent = 0;
360
226
 
361
- this.getIndent = function() {
362
- return new I(this.indent);
363
- };
364
- this.writeIndent = function() {this.write(this.getIndent());};
365
- this.goIndent = function(fn) {
366
- this.indent++;
367
- fn();
368
- this.indent--;
369
- };
227
+ this.indent = 0;
370
228
  this.write = function(...args) {
371
229
  for(let i of args) {
372
- if(i === true) this.result.push(this.getIndent());
373
- else this.result.push(i);
230
+ if(i === true) node.$result.push(new I(this.indent));
231
+ else node.$result.push(i);
374
232
  }
375
233
  };
376
- this.writeLine = function(s) {
377
- this.write(true, s + '\n');
234
+ this.writeLine = function(s) {this.write(true, s);};
235
+ this.writeIndent = function() {this.write(true);};
236
+ this.goIndent = fn => {
237
+ this.indent++;
238
+ fn();
239
+ this.indent--;
378
240
  };
379
- this._compile = function() {
380
- let result = this.result.slice();
381
- let dyn, prevDyn = 0, index = 99;
382
-
383
- for(;index>0;index--) {
384
- dyn = 0;
385
- let parts = result.slice();
386
- result = [];
387
- parts.forEach(n => {
388
- if(n.node) {
389
- dyn++;
390
- let r = this.subBuild(n.node, n.indent);
391
- if(r?.length) result.push(...r);
392
- } else result.push(n);
393
- });
394
- if(dyn == 0) break;
395
- if(dyn == prevDyn) throw 'Compile error: circular dependencies';
396
- prevDyn = dyn;
397
- }
398
- if(index <= 0) throw 'Compile error: circular dependencies';
399
241
 
400
- return result;
242
+ this.add = this.build = function(n) {
243
+ if(n === null) return;
244
+ assert(n instanceof xNode);
245
+ assert(!n.$inserted, 'already inserted');
246
+ node.$result.push({node: n, indent: this.indent});
247
+ n.$inserted = true;
401
248
  };
402
- this.toString = function() {
403
- let result = this._compile();
404
- return result.map(i => {
405
- if(i instanceof I) {
406
- let r = '', l = i.$indent;
407
- while(l--) r += ' ';
408
- return r;
249
+
250
+ this.isEmpty = function(n) {
251
+ if(n == null) return true;
252
+ assert(n.$done, 'Node is not built');
253
+ return !n.$result.some(r => {
254
+ if(typeof(r) == 'string') return true;
255
+ else if(r.node instanceof xNode) return !this.isEmpty(r.node);
256
+ else if(r instanceof I) return true;
257
+ else {
258
+ console.error('Type', r);
259
+ throw 'error type';
409
260
  }
410
- return i;
411
- }).join('');
261
+ });
412
262
  };
413
- this.build = function(node) {
414
- if(node == null) return;
415
- if(node.$deps?.length) {
416
- if(node.$deps.some(n => !n.$done)) {
417
- this.result.push({node, indent: this.indent});
418
- return;
263
+ }
264
+
265
+ function xBuild(ctx, node) {
266
+ let pending = 0;
267
+ const resolve = n => {
268
+ n.$compile?.forEach(c => {
269
+ c != null && resolve(c);
270
+ });
271
+ if(!n.$done) {
272
+ let ready = true;
273
+ if(n.$deps?.length) {
274
+ if(n.$deps.some(i => i != null && !i.$done)) {
275
+ pending++;
276
+ ready = false;
277
+ }
278
+ }
279
+ if(ready) {
280
+ let w = new xWriter(ctx, n);
281
+ n.$handler(w, n);
282
+ n.$done = true;
419
283
  }
420
284
  }
421
- node.handler(this, node);
422
- node.$done = true;
423
- };
424
- this.subBuild = function(node, indent=0) {
425
- let w = new xWriter(this._ctx);
426
- w.indent = indent;
427
- w.build(node);
428
- let r = w._compile();
429
- return r.length ? r : null;
285
+
286
+ if(n.$done) {
287
+ n.$result.forEach(r => {
288
+ if(r?.node instanceof xNode) resolve(r.node);
289
+ });
290
+ } else pending++;
430
291
  };
431
- this.addBlock = function(b) {
432
- b && b.forEach(i => {
433
- if(i instanceof I) i.$indent += this.indent;
434
- this.result.push(i);
292
+ let depth;
293
+ for(depth=10;depth > 0;depth--) {
294
+ pending = 0;
295
+ resolve(node);
296
+ if(!pending) break;
297
+ }
298
+ if(!depth) throw new Error('xNode: Circular dependency');
299
+
300
+ let result = [];
301
+
302
+ const asm = (n, baseIndent) => {
303
+ if(!n.$done) {
304
+ console.log('not resolved', n);
305
+ throw 'node is not resolved';
306
+ }
307
+ n.$result.forEach(r => {
308
+ if(typeof(r) == 'string') result.push(r);
309
+ else if(r.node instanceof xNode) {
310
+ asm(r.node, r.indent + baseIndent);
311
+ }
312
+ else if(r instanceof I) {
313
+ r.$indent += baseIndent;
314
+ result.push(r);
315
+ } else {
316
+ console.error('Type', r);
317
+ throw 'error type';
318
+ }
435
319
  });
436
320
  };
321
+ asm(node, 0);
322
+
323
+ for(let i = 0; i < result.length; i++) {
324
+ let r = result[i];
325
+ let next = result[i+1];
326
+
327
+ if(r instanceof I) {
328
+ if(next instanceof I) {
329
+ result[i] = '';
330
+ } else {
331
+ let s = '\n';
332
+ let j = r.$indent;
333
+ while(j--) {
334
+ s += ' ';
335
+ }
336
+ result[i] = s;
337
+ }
338
+ }
339
+ }
340
+
341
+ return result.join('');
437
342
  }
438
343
 
344
+
345
+ const noop = () => {};
346
+
347
+
439
348
  function xNode(_type, _data, _handler) {
440
349
  /*
441
350
  xNode(type, data, handler)
@@ -448,7 +357,10 @@
448
357
  let type, data, handler;
449
358
  if(typeof _type == 'string') {
450
359
  type = _type;
451
- if(typeof _data == 'function') {
360
+ if(_data === false && !_handler) {
361
+ handler = noop;
362
+ data = null;
363
+ } else if(typeof _data == 'function') {
452
364
  assert(!_handler);
453
365
  handler = _data;
454
366
  } else {
@@ -474,8 +386,20 @@
474
386
  assert(handler);
475
387
  }
476
388
 
477
- this.type = type;
478
- this.handler = handler;
389
+ this.$type = type;
390
+ this.$handler = handler;
391
+ this.$done = false;
392
+ this.$inserted = false;
393
+ this.$result = [];
394
+ this.$depends = function(n) {
395
+ assert(!this.$done, 'Attempt to add dependecy, but node is already resolved');
396
+ if(!this.$deps) this.$deps = [];
397
+ this.$deps.push(n);
398
+ };
399
+ this.$value = function(value) {
400
+ assert(!this.$done, 'Attempt to set active, depends node is already resolved');
401
+ this.value = value === undefined ? true : value;
402
+ };
479
403
  return this;
480
404
  }
481
405
 
@@ -515,7 +439,7 @@
515
439
  xNode.init.block.init(node);
516
440
  },
517
441
  handler: (ctx, node) => {
518
- if(!node.inline) ctx.writeIndent();
442
+ if(!node.inline) ctx.write(true);
519
443
 
520
444
  if(node.arrow) {
521
445
  if(node.name) ctx.write(`let ${node.name} = `);
@@ -525,7 +449,7 @@
525
449
  }
526
450
  ctx.write(`(${node.args.join(', ')}) `);
527
451
  if(node.arrow) ctx.write('=> ');
528
- ctx.write(`{\n`);
452
+ ctx.write(`{`, true);
529
453
  ctx.indent++;
530
454
  xNode.init.block.handler(ctx, node);
531
455
  ctx.indent--;
@@ -541,10 +465,11 @@
541
465
  node.voidTag = false;
542
466
 
543
467
  node.bindName = xNode.init.node.bindName;
468
+ node.getLast = () => last(node.children);
544
469
  node.push = function(n) {
545
470
  if(typeof n == 'string') {
546
471
  let p = last(this.children);
547
- if(p && p.type == 'node:text') {
472
+ if(p && p.$type == 'node:text') {
548
473
  p.value += n;
549
474
  return p;
550
475
  }
@@ -575,7 +500,12 @@
575
500
  });
576
501
  }
577
502
 
578
- let className = Array.from(node.class).join(' ');
503
+ let className = {};
504
+ node.class.forEach(sel => {
505
+ if(sel.$selector) sel = ctx._ctx.css.resolve(sel);
506
+ className[sel] = true;
507
+ });
508
+ className = Object.keys(className).join(' ');
579
509
  if(className) ctx.write(` class="${className}"`);
580
510
 
581
511
  if(node.children.length) {
@@ -612,11 +542,13 @@
612
542
  },
613
543
  template: (ctx, node) => {
614
544
  let template = ctx._ctx.xBuild(node.body);
615
- let convert;
616
- if(node.svg) convert = '$runtime.svgToFragment';
617
- else if(!template.match(/[<>]/)) {
545
+ let convert, cloneNode = node.cloneNode;
546
+ if(node.svg) {
547
+ convert = '$runtime.svgToFragment';
548
+ cloneNode = false;
549
+ } else if(!template.match(/[<>]/) && !node.requireFragment) {
618
550
  convert = '$runtime.createTextNode';
619
- if(!node.raw) template = htmlEntitiesToText(template);
551
+ cloneNode = false;
620
552
  } else {
621
553
  convert = '$$htmlToFragment';
622
554
  template = template.replace(/<!---->/g, '<>');
@@ -624,33 +556,177 @@
624
556
  if(node.raw) {
625
557
  ctx.write(ctx._ctx.Q(template));
626
558
  } else if(node.inline) {
627
- ctx.write(`${convert}(\`${ctx._ctx.Q(template)}\`)`);
559
+ ctx.write(`${convert}(\`${ctx._ctx.Q(template)}\``);
560
+ if(cloneNode || node.requireFragment) {
561
+ let opt = (cloneNode ? 1 : 0) + (node.requireFragment ? 2 : 0);
562
+ ctx.write(`, ${opt})`);
563
+ } else ctx.write(')');
628
564
  } else {
629
565
  assert(node.name);
630
- ctx.writeLine(`const ${node.name} = ${convert}(\`${ctx._ctx.Q(template)}\`);`);
566
+ ctx.write(true, `const ${node.name} = ${convert}(\`${ctx._ctx.Q(template)}\``);
567
+ if(cloneNode || node.requireFragment) {
568
+ let opt = (cloneNode ? 1 : 0) + (node.requireFragment ? 2 : 0);
569
+ ctx.write(`, ${opt});`);
570
+ } else ctx.write(');');
631
571
  }
632
572
  }
633
573
  };
634
574
 
575
+ function compactDOM() {
576
+ let data = this.DOM;
577
+ const details = {
578
+ node: [n => n.body],
579
+ each: [n => n.body],
580
+ slot: [n => n.body],
581
+ fragment: [n => n.body],
582
+ if: [n => n.body, n => n.bodyMain],
583
+ await: [n => n.parts.main, n => n.parts.then, n => n.parts.catch]
584
+ };
635
585
 
636
- const htmlEntitiesToText = (text) => {
637
- let entities = [
638
- [/&amp;/g, '&'],
639
- [/&apos;/g, '\''],
640
- [/&#x27;/g, '\''],
641
- [/&#x2F;/g, '/'],
642
- [/&#39;/g, '\''],
643
- [/&#47;/g, '/'],
644
- [/&lt;/g, '<'],
645
- [/&gt;/g, '>'],
646
- [/&nbsp;/g, ' '],
647
- [/&quot;/g, '"']
648
- ];
649
- entities.forEach(([k, v]) => {
650
- text = text.replace(k, v);
651
- });
652
- return text;
653
- };
586
+ function go(body, parentNode) {
587
+ let i;
588
+
589
+ const getPrev = () => {
590
+ return i > 0 && body.length ? body[i - 1] : null;
591
+ };
592
+
593
+ const getNext = () => {
594
+ return i < body.length ? body[i + 1] : null;
595
+ };
596
+
597
+ for(i=0; i<body.length; i++) {
598
+ let node = body[i];
599
+ if(node.type == 'text') {
600
+ let next = getNext();
601
+ if(next && next.type == 'text') {
602
+ node.value += next.value;
603
+ body.splice(i + 1, 1);
604
+ }
605
+
606
+ if(node.value) {
607
+ if(!node.value.trim()) {
608
+ node.value = ' ';
609
+ } else {
610
+ let rx = node.value.match(/^(\s*)(.*?)(\s*)$/);
611
+ if(rx) {
612
+ let r = '';
613
+ if(rx[1]) r += ' ';
614
+ r += rx[2];
615
+ if(rx[3]) r += ' ';
616
+ node.value = r;
617
+ }
618
+ }
619
+ }
620
+ } else {
621
+ if(node.type == 'node' && (node.name == 'pre' || node.name == 'textarea')) continue;
622
+ let keys = details[node.type];
623
+ keys && keys.forEach(k => {
624
+ let body = k(node);
625
+ if(body && body.length) go(body, node);
626
+ });
627
+ }
628
+ }
629
+
630
+ const isTable = n => ['thead', 'tbody', 'tfoot', 'tr', 'td', 'th'].includes(n.name);
631
+
632
+ i = 0;
633
+ while(i < body.length) {
634
+ let node = body[i];
635
+ if(node.type == 'text' && !node.value.trim()) {
636
+ if(parentNode && (parentNode.name == 'table' || isTable(parentNode)) && (i == 0 || i == body.length -1)) {
637
+ body.splice(i, 1);
638
+ continue;
639
+ }
640
+
641
+ let prev = getPrev();
642
+ let next = getNext();
643
+
644
+ if(next?.type == 'node' && ['br', 'div'].includes(next.name)) {
645
+ body.splice(i, 1);
646
+ continue;
647
+ }
648
+
649
+ if(prev?.type == 'node' && ['br', 'div'].includes(prev.name)) {
650
+ body.splice(i, 1);
651
+ continue;
652
+ }
653
+
654
+ if(prev && next) {
655
+ if(prev.type == 'node' && next.type == 'node') {
656
+ if(isTable(prev) && isTable(next) ||
657
+ prev.name == 'li' && next.name == 'li' ||
658
+ prev.name == 'div' && next.name == 'div') {
659
+ body.splice(i, 1);
660
+ continue;
661
+ }
662
+ }
663
+ } else if(parentNode) {
664
+ let p = prev && prev.type == 'node' && prev.name;
665
+ let n = next && next.type == 'node' && next.name;
666
+
667
+ if((p == 'td' || n == 'td') && ((parentNode.type == 'node' && parentNode.name == 'tr') || (parentNode.type == 'each'))) {
668
+ body.splice(i, 1);
669
+ continue;
670
+ }
671
+ if((p == 'tbody' || n == 'tbody') && (parentNode.type == 'node' && parentNode.name == 'table')) {
672
+ body.splice(i, 1);
673
+ continue;
674
+ }
675
+ if((p == 'li' || n == 'li') && (parentNode.type == 'node' && parentNode.name == 'ul')) {
676
+ body.splice(i, 1);
677
+ continue;
678
+ }
679
+ if(parentNode.type == 'node' && parentNode.name == 'div') {
680
+ body.splice(i, 1);
681
+ continue;
682
+ }
683
+ if(parentNode.type == 'node' && (prev && prev.type == 'each' || next && next.type == 'each')) {
684
+ body.splice(i, 1);
685
+ continue;
686
+ }
687
+ if(parentNode.type == 'node' && parentNode.name == 'button' && (!p || !n)) {
688
+ body.splice(i, 1);
689
+ continue;
690
+ }
691
+ }
692
+ }
693
+ i++;
694
+ }
695
+
696
+ }
697
+
698
+ function trimNodes(srcNodes) {
699
+ let nodes = srcNodes.slice();
700
+ let ex = [];
701
+ while(nodes.length) {
702
+ let n = nodes[0];
703
+ if(n.type == 'fragment' || n.type == 'comment') {
704
+ ex.push(n);
705
+ nodes.shift();
706
+ continue;
707
+ }
708
+ if(n.type == 'text' && !n.value.trim()) nodes.shift();
709
+ else break;
710
+ }
711
+ nodes = [...ex, ...nodes];
712
+ ex = [];
713
+ while(nodes.length) {
714
+ let n = last(nodes);
715
+ if(n.type == 'fragment' || n.type == 'comment') {
716
+ ex.push(n);
717
+ nodes.pop();
718
+ continue;
719
+ }
720
+ if(n.type == 'text' && !n.value.trim()) nodes.pop();
721
+ else break;
722
+ }
723
+ return [...nodes, ...ex];
724
+ }
725
+
726
+ data.body = trimNodes(data.body);
727
+
728
+ go(data.body);
729
+ }
654
730
 
655
731
  function parse() {
656
732
  let source = this.source;
@@ -739,7 +815,7 @@
739
815
  }
740
816
  }
741
817
  if(begin) {
742
- if(a.match(/[\da-zA-Z^\-]/)) {
818
+ if(a.match(/[\da-zA-Z^]/)) {
743
819
  name += a;
744
820
  continue;
745
821
  } else {
@@ -1101,13 +1177,11 @@
1101
1177
  watchers: [],
1102
1178
  imports: [],
1103
1179
  importedNames: [],
1104
- autosubscribeNames: [],
1105
1180
  props: [],
1106
1181
  rootVariables: {},
1107
1182
  rootFunctions: {},
1108
1183
  readOnly: false,
1109
- autoimport: {},
1110
- comments: []
1184
+ autoimport: {}
1111
1185
  };
1112
1186
  if(source) {
1113
1187
  this.script.readOnly = this.scriptNodes.some(n => n.attributes.some(a => a.name == 'read-only'));
@@ -1121,11 +1195,7 @@
1121
1195
  return rx[1] + '$$_noCheck;';
1122
1196
  }).join('\n');
1123
1197
  }
1124
- const onComment = (isBlockComment, value, start, end) => {
1125
- if(isBlockComment) return;
1126
- this.script.comments.push({start, end, value});
1127
- };
1128
- this.script.ast = acorn.parse(source, {sourceType: 'module', ecmaVersion: 12, onComment});
1198
+ this.script.ast = acorn.parse(source, {sourceType: 'module', ecmaVersion: 12});
1129
1199
 
1130
1200
  if(source.includes('$props')) this.require('$props');
1131
1201
  if(source.includes('$attributes')) this.require('$attributes');
@@ -1286,7 +1356,7 @@
1286
1356
 
1287
1357
  const makeWatch = (n) => {
1288
1358
  function assertExpression(n) {
1289
- if(['Identifier', 'TemplateLiteral', 'Literal'].includes(n.type)) return;
1359
+ if(n.type == 'Identifier') return;
1290
1360
  if(n.type.endsWith('Expression')) return;
1291
1361
  throw 'Wrong expression';
1292
1362
  }
@@ -1332,31 +1402,14 @@
1332
1402
  let lastPropIndex = null;
1333
1403
  let constantProps = true;
1334
1404
 
1335
- if(result.comments.length) {
1336
- result.comments.forEach(c => {
1337
- for(let i = 1; i < ast.body.length; i++) {
1338
- let p = ast.body[i-1];
1339
- let n = ast.body[i];
1340
- if(p.end < c.start && c.start < n.start) {
1341
- p._comment = c.value;
1342
- return;
1343
- }
1344
- }
1345
- });
1346
- }
1347
-
1348
1405
  ast.body.forEach(n => {
1349
1406
  if(n.type == 'ImportDeclaration') {
1350
1407
  imports.push(n);
1351
1408
  n.specifiers.forEach(s => {
1352
1409
  if(s.local.type != 'Identifier') return;
1353
- let name = s.local.name;
1354
- result.importedNames.push(name);
1355
- if(name[0].toLowerCase() == name[0]) {
1356
- if(!n._comment || !n._comment.includes('!no-autosubscribe')) result.autosubscribeNames.push(s.local.name);
1357
- }
1410
+ result.importedNames.push(s.local.name);
1358
1411
  if(s.type != 'ImportDefaultSpecifier') return;
1359
- result.imports.push(name);
1412
+ result.imports.push(s.local.name);
1360
1413
  });
1361
1414
  return;
1362
1415
  } else if(n.type == 'ExportNamedDeclaration') {
@@ -1390,11 +1443,12 @@
1390
1443
  resultBody.push(n);
1391
1444
  });
1392
1445
 
1446
+ this.glob.component.$handler = (ctx, n) => {
1447
+ if(this.inuse.$component || n.value) ctx.writeLine('const $component = $runtime.current_component;');
1448
+ };
1449
+ this.module.head.push(this.glob.component);
1450
+
1393
1451
  let header = [];
1394
- header.push(rawNode(() => {
1395
- if(this.inuse.$component) return 'const $component = $runtime.current_component;';
1396
- }));
1397
-
1398
1452
  header.push(rawNode(() => {
1399
1453
  if(this.inuse.$events) return 'const $events = $option.events || {};';
1400
1454
  }));
@@ -1404,7 +1458,7 @@
1404
1458
  if(this.inuse.$props) return 'let $props = $option.props || {};';
1405
1459
  }));
1406
1460
 
1407
- if(!constantProps && !this.script.readOnly) this.require('apply', '$cd');
1461
+ if(!constantProps && !this.script.readOnly) this.require('apply');
1408
1462
 
1409
1463
  resultBody.splice(lastPropIndex, 0, rawNode(() => {
1410
1464
  let code = [];
@@ -1463,11 +1517,14 @@
1463
1517
  if(this.inuse.$onDestroy) return `const $onDestroy = fn => $component._d.push(fn);`;
1464
1518
  }));
1465
1519
 
1466
- if(this.config.autoSubscribe && result.autosubscribeNames.length) {
1467
- if(!this.script.readOnly) this.require('$cd', 'apply');
1468
- header.push(rawNode(() => {
1469
- if(this.inuse.apply) return `$runtime.autoSubscribe(${result.autosubscribeNames.join(', ')});`;
1470
- }));
1520
+ if(this.config.autoSubscribe) {
1521
+ let names = result.importedNames.filter(name => name[0].toLowerCase() == name[0]);
1522
+ if(names.length) {
1523
+ if(!this.script.readOnly) this.require('$cd', 'apply');
1524
+ header.push(rawNode(() => {
1525
+ if(this.inuse.apply) return `$runtime.autoSubscribe(${names.join(', ')});`;
1526
+ }));
1527
+ }
1471
1528
  }
1472
1529
 
1473
1530
  if(!rootFunctions.$emit) {
@@ -1555,21 +1612,38 @@
1555
1612
  let code = astring.generate({
1556
1613
  type: 'CustomBlock',
1557
1614
  body: node.body
1558
- }, {generator, startingIndentLevel: ctx.indent});
1559
- ctx.write(code);
1615
+ }, {generator, startingIndentLevel: 0});
1616
+ code.split(/\n/).forEach(s => {
1617
+ if(s) ctx.write(true, s);
1618
+ });
1560
1619
  };
1561
1620
 
1562
1621
  function buildRuntime() {
1563
- let runtime = xNode('block', {scope: true});
1564
- runtime.push(xNode((ctx) => {
1565
- if(this.inuse.$cd) ctx.writeLine('let $cd = $component.$cd;');
1566
- }));
1622
+ let runtime = xNode('block', {scope: true, $compile: []});
1623
+
1624
+ let rootCD = this.glob.rootCD;
1625
+ rootCD.$handler = (ctx, n) => {
1626
+ n.$value(!!n.$deps[0].value);
1627
+ if(n.value) {
1628
+ ctx.writeLine('let $cd = $component.$cd;');
1629
+ this.glob.component.$value(true);
1630
+ }
1631
+ };
1632
+ runtime.push(rootCD);
1633
+ this.glob.component.$depends(rootCD);
1567
1634
 
1568
- let bb = this.buildBlock(this.DOM, {inline: true});
1569
- runtime.push(xNode('template', {
1570
- name: '$parentElement',
1571
- body: bb.tpl,
1572
- svg: bb.svg
1635
+ let bb = this.buildBlock(this.DOM, {
1636
+ inline: true,
1637
+ template: {
1638
+ name: '$parentElement',
1639
+ cloneNode: true
1640
+ }
1641
+ });
1642
+ bb.requireCD && rootCD.$depends(bb.requireCD);
1643
+ runtime.push(bb.template);
1644
+ runtime.push(xNode('root-event', (ctx) => {
1645
+ if(!this.inuse.rootEvent) return;
1646
+ ctx.write(true, `const $$addRootEvent = $runtime.makeRootEvent($parentElement);`);
1573
1647
  }));
1574
1648
  runtime.push(bb.source);
1575
1649
 
@@ -1592,8 +1666,10 @@
1592
1666
  }
1593
1667
  }));
1594
1668
 
1595
- runtime.push(xNode('bind-component-element', (ctx) => {
1596
- if(ctx.inuse.$insertElementByOption) ctx.writeLine('$runtime.$insertElementByOption($element, $option, $parentElement);');
1669
+ runtime.push(xNode('bind-component-element', {
1670
+ $deps: [this.glob.componentFn]
1671
+ }, (ctx) => {
1672
+ if(this.glob.componentFn.value == 'thin') ctx.writeLine(`return {$dom: $parentElement};`);
1597
1673
  else ctx.writeLine('return $parentElement;');
1598
1674
  }));
1599
1675
 
@@ -1627,17 +1703,34 @@
1627
1703
 
1628
1704
  function buildBlock(data, option={}) {
1629
1705
  let rootTemplate = xNode('node', {inline: true, _ctx: this});
1706
+ let rootSVG = false, requireFragment = option.template?.requireFragment;
1630
1707
  let binds = xNode('block');
1631
1708
  let result = {};
1709
+ let requireCD = result.requireCD = xNode('require-cd', false);
1632
1710
  let inuse = Object.assign({}, this.inuse);
1633
1711
 
1712
+ if(!option.parentElement) option.parentElement = '$parentElement';
1713
+
1714
+ if(option.each?.blockPrefix) binds.push(option.each.blockPrefix);
1715
+
1716
+ if(option.allowSingleBlock && data.body.length == 1) {
1717
+ let n = data.body[0];
1718
+ if(n.type == 'node' && n.name.match(/^[A-Z]/)) {
1719
+ let component = this.makeComponent(n, requireCD);
1720
+ return {
1721
+ requireCD,
1722
+ singleBlock: component.bind
1723
+ }
1724
+ }
1725
+ }
1726
+
1634
1727
  const go = (data, isRoot, tpl) => {
1635
1728
  let body = data.body.filter(n => {
1636
1729
  if(n.type == 'script' || n.type == 'style' || n.type == 'slot') return false;
1637
1730
  if(n.type == 'comment' && !this.config.preserveComments) return false;
1638
1731
  if(n.type == 'fragment') {
1639
1732
  try {
1640
- let f = this.makeFragment(n);
1733
+ let f = this.makeFragment(n, requireCD);
1641
1734
  f && binds.push(f);
1642
1735
  } catch (e) {
1643
1736
  wrapException(e, n);
@@ -1650,7 +1743,7 @@
1650
1743
  if(tpl.name == 'table') {
1651
1744
  let result = [], tbody = null;
1652
1745
  body.forEach(n => {
1653
- if(n.type == 'node' && ['thead', 'tbody', 'tfoot', 'colgroup'].includes(n.name)) {
1746
+ if(n.type == 'node' && ['thead', 'tbody', 'tfoot'].includes(n.name)) {
1654
1747
  result.push(n);
1655
1748
  tbody = null;
1656
1749
  return;
@@ -1682,11 +1775,29 @@
1682
1775
  if(svgElements[node.name]) svg = true;
1683
1776
  else return other = true;
1684
1777
  });
1685
- if(svg && !other) result.svg = true;
1778
+ if(svg && !other) rootSVG = true;
1686
1779
  }
1687
1780
 
1781
+ let lastStatic;
1782
+
1783
+ const placeLabel = name => {
1784
+ let el;
1785
+ if(lastStatic) {
1786
+ el = lastStatic;
1787
+ el.label = true;
1788
+ lastStatic = null;
1789
+ } else {
1790
+ el = xNode('node:comment', {label: true, value: name});
1791
+ tpl.push(el);
1792
+ }
1793
+ return el;
1794
+ };
1795
+
1688
1796
  const bindNode = (n) => {
1689
1797
  if(n.type === 'text') {
1798
+ let prev = tpl.getLast();
1799
+ if(prev?.$type == 'node:text' && prev._boundName) tpl.push(xNode('node:comment', {label: true}));
1800
+
1690
1801
  if(n.value.indexOf('{') >= 0) {
1691
1802
  const pe = this.parseText(n.value);
1692
1803
  this.detectDependency(pe);
@@ -1696,13 +1807,18 @@
1696
1807
  textNode = tpl.push(pe.staticText);
1697
1808
  } else {
1698
1809
  textNode = tpl.push(' ');
1699
- binds.push(xNode('bindText', {
1810
+ let bindText = xNode('bindText', {
1811
+ $deps: [this.glob.apply],
1700
1812
  el: textNode.bindName(),
1701
1813
  exp: pe.result
1702
1814
  }, (ctx, n) => {
1703
- if(this.inuse.$cd) ctx.writeLine(`$runtime.bindText($cd, ${n.el}, () => ${n.exp});`);
1704
- else ctx.writeLine(`${n.el}.textContent = ${n.exp};`);
1705
- }));
1815
+ if(this.glob.apply.value) {
1816
+ requireCD.$value(true);
1817
+ ctx.writeLine(`$runtime.bindText($cd, ${n.el}, () => ${n.exp});`);
1818
+ } else ctx.writeLine(`${n.el}.textContent = ${n.exp};`);
1819
+ });
1820
+ binds.push(bindText);
1821
+ requireCD.$depends(bindText);
1706
1822
  }
1707
1823
 
1708
1824
  pe.parts.forEach(p => {
@@ -1714,32 +1830,46 @@
1714
1830
  ]}));
1715
1831
  });
1716
1832
 
1833
+ lastStatic = textNode;
1717
1834
  } else {
1718
- tpl.push(n.value);
1835
+ lastStatic = tpl.push(n.value);
1719
1836
  }
1720
1837
  } else if(n.type === 'template') {
1838
+ lastStatic = null;
1721
1839
  tpl.push(n.openTag);
1722
1840
  tpl.push(n.content);
1723
1841
  tpl.push('</template>');
1724
1842
  } else if(n.type === 'node') {
1725
1843
  if(n.name == 'malina' && !option.malinaElement) {
1726
1844
  let b;
1727
- if(n.elArg == 'portal') b = this.attachPortal(n);
1728
- else b = this.attachHead(n);
1845
+ if(n.elArg == 'portal') b = this.attachPortal(n, requireCD);
1846
+ else b = this.attachHead(n, requireCD);
1729
1847
  b && binds.push(b);
1730
1848
  return;
1731
1849
  }
1732
1850
  if(n.name == 'component' || n.name.match(/^[A-Z]/)) {
1733
1851
  if(n.name == 'component' || !n.elArg) {
1734
1852
  // component
1735
- let el = xNode('node:comment', {label: true, value: n.name});
1736
- tpl.push(el);
1737
- let b = this.makeComponent(n, el);
1738
- binds.push(b.bind);
1853
+ if(isRoot) requireFragment = true;
1854
+ let el = placeLabel(n.name);
1855
+
1856
+ if(n.name == 'component') {
1857
+ // dyn-component
1858
+ binds.push(this.makeComponentDyn(n, requireCD, el));
1859
+ } else {
1860
+ let component = this.makeComponent(n, requireCD);
1861
+ binds.push(xNode('attach-component', {
1862
+ component: component.bind,
1863
+ el: el.bindName()
1864
+ }, (ctx, n) => {
1865
+ ctx.write(true, `$runtime.attachBlock($cd, ${n.el}, `);
1866
+ ctx.add(n.component);
1867
+ ctx.write(')');
1868
+ }));
1869
+ }
1739
1870
  } else {
1740
- let el = xNode('node:comment', {label: true, value: `exported ${n.elArg}`});
1741
- tpl.push(el);
1742
- let b = this.attchExportedFragment(n, el, n.name);
1871
+ let el = placeLabel(`exported ${n.elArg}`);
1872
+ let b = this.attchExportedFragment(n, el, n.name, requireCD);
1743
1873
  b && binds.push(b);
1744
1874
  }
1745
1875
  return;
@@ -1748,33 +1878,52 @@
1748
1878
  let slotName = n.elArg;
1749
1879
  if(!slotName) {
1750
1880
  if(option.context == 'fragment') {
1751
- let el = xNode('node:comment', {label: true, value: 'fragment-slot'});
1752
- tpl.push(el);
1753
- binds.push(this.attachFragmentSlot(el));
1881
+ let el = placeLabel('fragment-slot');
1882
+ binds.push(this.attachFragmentSlot(el, requireCD));
1754
1883
  return;
1755
1884
  } else slotName = 'default';
1756
1885
  }
1757
- let el = xNode('node:comment', {label: true, value: slotName});
1758
- tpl.push(el);
1759
- binds.push(this.attachSlot(slotName, el, n));
1886
+
1887
+ let el = placeLabel(slotName);
1888
+ let slot = this.attachSlot(slotName, n, requireCD);
1889
+
1890
+ binds.push(xNode('attach-slot', {
1891
+ $deps: [requireCD],
1892
+ $compile: [slot],
1893
+ el: el.bindName(),
1894
+ slot,
1895
+ requireCD
1896
+ }, (ctx, n) => {
1897
+ if(n.requireCD.value) ctx.write(true, `$runtime.attachBlock($cd, ${n.el}, `);
1898
+ else ctx.write(true, `$runtime.attachBlock($component, ${n.el}, `);
1899
+ ctx.add(n.slot);
1900
+ ctx.write(');', true);
1901
+ }));
1760
1902
  return;
1761
1903
  }
1762
1904
  if(n.name == 'fragment') {
1905
+ requireCD.$value(true);
1763
1906
  assert(n.elArg, 'Fragment name is required');
1764
- let el = xNode('node:comment', {label: true, value: `fragment ${n.elArg}`});
1765
- tpl.push(el);
1766
- let b = this.attachFragment(n, el);
1767
- b && binds.push(b);
1907
+ let el = placeLabel(`fragment ${n.elArg}`);
1908
+ binds.push(xNode('attach-fragment', {
1909
+ el: el.bindName(),
1910
+ fragment: this.attachFragment(n)
1911
+ }, (ctx, n) => {
1912
+ ctx.write(true, `$runtime.attachBlock($cd, ${n.el}, `);
1913
+ ctx.add(n.fragment);
1914
+ ctx.write(`)`);
1915
+ }));
1768
1916
  return;
1769
1917
  }
1770
1918
 
1771
1919
  let el = xNode('node', {name: n.name});
1772
1920
  if(option.oneElement) el._boundName = option.oneElement;
1773
1921
  tpl.push(el);
1922
+ lastStatic = el;
1774
1923
 
1775
1924
  if(n.attributes.some(a => a.name.startsWith('{...'))) {
1776
1925
  n.spreading = [];
1777
- this.require('$cd');
1926
+ requireCD.$value(true);
1778
1927
  binds.push(xNode('spread-to-element', {
1779
1928
  el: el.bindName(),
1780
1929
  props: n.spreading
@@ -1784,7 +1933,7 @@
1784
1933
  }
1785
1934
  let bindTail = [];
1786
1935
  n.attributes.forEach(p => {
1787
- let b = this.bindProp(p, n, el);
1936
+ let b = this.bindProp(p, n, el, requireCD);
1788
1937
  if(b) {
1789
1938
  if(b.bind) binds.push(b.bind);
1790
1939
  if(b.bindTail) bindTail.push(b.bindTail);
@@ -1816,7 +1965,9 @@
1816
1965
  go(n, false, el);
1817
1966
  }
1818
1967
  } else if(n.type === 'each') {
1968
+ requireCD.$value(true);
1819
1969
  if(data.type == 'node' && data.body.length == 1) {
1970
+ lastStatic = null;
1820
1971
  let eachBlock = this.makeEachBlock(n, {
1821
1972
  elName: tpl.bindName(),
1822
1973
  onlyChild: true
@@ -1824,17 +1975,15 @@
1824
1975
  binds.push(eachBlock.source);
1825
1976
  return;
1826
1977
  } else {
1827
- let element = xNode('node:comment', {label: true, value: `${n.value}`});
1828
- tpl.push(element);
1978
+ if(isRoot) requireFragment = true;
1979
+ let element = placeLabel(n.value);
1829
1980
  let eachBlock = this.makeEachBlock(n, {elName: element.bindName()});
1830
1981
  binds.push(eachBlock.source);
1831
1982
  return;
1832
1983
  }
1833
1984
  } else if(n.type === 'if') {
1834
- let element = xNode('node:comment', {label: true, value: n.value});
1835
- tpl.push(element);
1836
- let ifBlock = this.makeifBlock(n, element);
1837
- binds.push(ifBlock.source);
1985
+ if(isRoot) requireFragment = true;
1986
+ binds.push(this.makeifBlock(n, placeLabel(n.value), requireCD));
1838
1987
  return;
1839
1988
  } else if(n.type === 'systag') {
1840
1989
  let r = n.value.match(/^@(\w+)\s+(.*)$/s);
@@ -1842,18 +1991,20 @@
1842
1991
  let exp = r[2];
1843
1992
 
1844
1993
  if(name == 'html') {
1845
- let el = xNode('node:comment', {label: true, value: 'html'});
1846
- tpl.push(el);
1847
- binds.push(this.makeHtmlBlock(exp, el));
1994
+ if(isRoot) requireFragment = true;
1995
+ let el = placeLabel('html');
1996
+ binds.push(this.makeHtmlBlock(exp, el, requireCD));
1848
1997
  return;
1849
1998
  } else throw 'Wrong tag';
1850
1999
  } else if(n.type === 'await') {
1851
- let el = xNode('node:comment', {label: true, value: n.value});
1852
- tpl.push(el);
1853
- binds.push(this.makeAwaitBlock(n, el));
2000
+ if(isRoot) requireFragment = true;
2001
+ requireCD.$value(true);
2002
+ let el = placeLabel(n.value);
2003
+ let r = this.makeAwaitBlock(n, el);
2004
+ r && binds.push(r);
1854
2005
  return;
1855
2006
  } else if(n.type === 'comment') {
1856
- tpl.push(n.content);
2007
+ lastStatic = tpl.push(n.content);
1857
2008
  }
1858
2009
  };
1859
2010
  body.forEach(node => {
@@ -1866,57 +2017,111 @@
1866
2017
  };
1867
2018
  go(data, true, rootTemplate);
1868
2019
  if(option.protectLastTag) {
1869
- let l = last(rootTemplate.children);
1870
- if(l && l.type == 'node:comment' && l.label) {
2020
+ let l = rootTemplate.getLast();
2021
+ if(l?.label) {
1871
2022
  rootTemplate.push(xNode('node:comment', {value: ''}));
1872
2023
  }
1873
2024
  }
1874
2025
 
1875
- result.tpl = rootTemplate;
1876
-
2026
+ let innerBlock = null;
1877
2027
  if(binds.body.length) {
1878
- const innerBlock = xNode('block');
2028
+ binds.push(requireCD);
2029
+ innerBlock = xNode('block');
1879
2030
  if(!option.oneElement) {
1880
- innerBlock.push(xNode('bindNodes', ctx => {
2031
+ innerBlock.push(xNode('bindNodes', {
2032
+ tpl: rootTemplate,
2033
+ root: option.parentElement,
2034
+ single: rootTemplate.children.length == 1 && !requireFragment
2035
+ }, (ctx, n) => {
1881
2036
 
1882
2037
  const gen = (parent, parentName) => {
1883
2038
  for(let i=0; i < parent.children.length; i++) {
1884
2039
  let node = parent.children[i];
1885
2040
  let diff = i == 0 ? '[$runtime.firstChild]' : `[$runtime.childNodes][${i}]`;
1886
2041
 
1887
- if(node._boundName) ctx.writeLine(`let ${node._boundName} = ${parentName() + diff};`);
2042
+ if(node._boundName) ctx.write(true, `let ${node._boundName} = ${parentName() + diff};`);
1888
2043
  if(node.children) gen(node, () => {
1889
2044
  if(node._boundName) return node._boundName;
1890
2045
  return parentName() + diff;
1891
2046
  });
1892
2047
  }
1893
2048
  };
1894
- gen(rootTemplate, () => '$parentElement');
2049
+ if(n.single) {
2050
+ let node = n.tpl.children[0];
2051
+ if(node._boundName) ctx.write(true, `let ${node._boundName} = ${n.root};`);
2052
+ if(node.children) gen(node, () => n.root);
2053
+ } else {
2054
+ gen(n.tpl, () => n.root);
2055
+ }
1895
2056
  }));
1896
2057
  }
1897
2058
  innerBlock.push(binds);
1898
2059
 
1899
2060
  if(option.inline) {
1900
2061
  result.source = innerBlock;
1901
- } else if(option.inlineFunction) {
1902
- result.source = xNode('function', {
1903
- inline: true,
1904
- arrow: true,
1905
- args: ['$cd', '$parentElement'].concat(option.args || []),
1906
- body: [innerBlock]
1907
- });
1908
- } else {
1909
- result.name = '$$build' + (this.uniqIndex++);
1910
- result.source = xNode('function', {
1911
- name: result.name,
1912
- args: ['$cd', '$parentElement'].concat(option.args || []),
1913
- body: [innerBlock]
1914
- });
1915
2062
  }
1916
2063
  } else {
2064
+ result.requireCD.$done = true;
1917
2065
  result.name = '$runtime.noop';
1918
2066
  result.source = null;
1919
2067
  }
2068
+
2069
+ if(!option.inline) {
2070
+ let template = xNode('template', {
2071
+ body: rootTemplate,
2072
+ svg: rootSVG,
2073
+ requireFragment
2074
+ });
2075
+ if(option.template) Object.assign(template, option.template);
2076
+ else template.inline = true;
2077
+
2078
+ result.block = xNode('block', {
2079
+ $compile: [innerBlock, requireCD],
2080
+ $deps: [requireCD],
2081
+ requireCD,
2082
+ innerBlock,
2083
+ tpl: template,
2084
+ each: option.each,
2085
+ parentElement: option.parentElement
2086
+ }, (ctx, n) => {
2087
+ if(n.each && !ctx.isEmpty(n.innerBlock)) {
2088
+ if(n.requireCD.value) ctx.write(`$runtime.makeEachBlock(`);
2089
+ else ctx.write(`$runtime.makeStaticEachBlock(`);
2090
+ } else {
2091
+ if(n.requireCD.value) ctx.write(`$runtime.makeBlock(`);
2092
+ else ctx.write(`$runtime.makeStaticBlock(`);
2093
+ }
2094
+ ctx.add(n.tpl);
2095
+ if(!ctx.isEmpty(n.innerBlock)) {
2096
+ if(n.each) {
2097
+ if(n.requireCD.value) ctx.write(`, ($cd, ${n.parentElement}, ${n.each.itemName}, ${n.each.indexName}) => {`, true);
2098
+ else ctx.write(`, (${n.parentElement}, ${n.each.itemName}, ${n.each.indexName}) => {`, true);
2099
+ } else {
2100
+ let extra = option.extraArguments ? ', ' + option.extraArguments.join(', ') : '';
2101
+ if(n.requireCD.value) ctx.write(`, ($cd, ${n.parentElement}${extra}) => {`, true);
2102
+ else ctx.write(`, (${n.parentElement}${extra}) => {`, true);
2103
+ }
2104
+ ctx.indent++;
2105
+ ctx.add(n.innerBlock);
2106
+ if(n.each?.rebind) {
2107
+ ctx.write(true, `return `);
2108
+ ctx.add(n.each.rebind);
2109
+ ctx.write(`;`, true);
2110
+ } ctx.indent--;
2111
+ ctx.write(true, `}`);
2112
+ }
2113
+ ctx.write(`)`);
2114
+ });
2115
+ } else {
2116
+ result.template = xNode('template', {
2117
+ body: rootTemplate,
2118
+ svg: rootSVG,
2119
+ requireFragment
2120
+ });
2121
+ if(option.template) Object.assign(result.template, option.template);
2122
+ else result.template.inline = true;
2123
+ }
2124
+
1920
2125
  result.inuse = {};
1921
2126
  for(let k in this.inuse) {
1922
2127
  result.inuse[k] = this.inuse[k] - (inuse[k] || 0);
@@ -3869,7 +4074,7 @@
3869
4074
  }
3870
4075
  } else cleanSelectorItems.push(s);
3871
4076
  }
3872
- while(cleanSelectorItems.length && ['WhiteSpace', 'Combinator'].includes(last(cleanSelectorItems).type)) cleanSelectorItems.pop();
4077
+ while(cleanSelectorItems.length && last(cleanSelectorItems).type == 'WhiteSpace') cleanSelectorItems.pop();
3873
4078
  if(!cleanSelectorItems.length || globalBlock) { // fully global?
3874
4079
  assert(origin.length);
3875
4080
  fullSelector.children = origin;
@@ -3894,7 +4099,8 @@
3894
4099
  cleanSelector,
3895
4100
  isSimple,
3896
4101
  source: [],
3897
- fullyGlobal: origin.every(i => i.global)
4102
+ fullyGlobal: origin.every(i => i.global),
4103
+ hashedSelectors: []
3898
4104
  };
3899
4105
  }
3900
4106
 
@@ -3902,17 +4108,18 @@
3902
4108
  assert(sobj.isSimple);
3903
4109
  if(!sobj.external) sobj.external = emptyBlock ? true : genId$1();
3904
4110
  } else if(!sobj.local) {
3905
- if(sobj.isSimple) sobj.local = genId$1();
3906
- else sobj.local = self.id;
4111
+ sobj.local = true;
3907
4112
  }
3908
4113
 
3909
- let hash = external ? sobj.external : sobj.local;
3910
4114
  if(emptyBlock) fullSelector.emptyBlock = true;
3911
4115
  sobj.source.push(fullSelector);
3912
4116
 
3913
4117
  let hashed = origin.slice();
4118
+ hashed._external = external;
4119
+ sobj.hashedSelectors.push(hashed);
4120
+
3914
4121
  const insert = (i) => {
3915
- hashed.splice(i, 0, {type: "ClassSelector", loc: null, name: hash, __hash: true});
4122
+ hashed.splice(i, 0, {type: "ClassSelector", loc: null, name: null, __hash: true});
3916
4123
  };
3917
4124
 
3918
4125
  for(let i=hashed.length-1;i>=0;i--) {
@@ -3940,6 +4147,7 @@
3940
4147
  self.markAsExternal = (name) => {
3941
4148
  let sobj = selectors['.' + name];
3942
4149
  if(!sobj) selectors['.' + name] = sobj = {isSimple: true, cleanSelector: '.' + name};
4150
+ assert(!sobj.resolved);
3943
4151
  if(!sobj.external) sobj.external = true;
3944
4152
  active = true;
3945
4153
  };
@@ -3953,7 +4161,36 @@
3953
4161
  });
3954
4162
  };
3955
4163
 
4164
+ let _hashesResolved = false;
4165
+ const resolveHashes = () => {
4166
+ if(_hashesResolved) return;
4167
+ _hashesResolved = true;
4168
+ Object.values(selectors).forEach(sel => {
4169
+ if(!sel.hashedSelectors) return;
4170
+ if(sel.resolved) return;
4171
+ sel.resolved = true;
4172
+ if(sel.external) {
4173
+ if(sel.local === true) {
4174
+ if(self.passingClass) sel.local = genId$1();
4175
+ else sel.local = self.id;
4176
+ } } else {
4177
+ assert(sel.local === true);
4178
+ if(self.passingClass) sel.local = genId$1();
4179
+ else sel.local = self.id;
4180
+ }
4181
+ sel.hashedSelectors.forEach(hashed => {
4182
+ let hash = hashed._external ? sel.external : sel.local;
4183
+ assert(hash);
4184
+ hashed.forEach(n => {
4185
+ if(!n.__hash) return;
4186
+ n.name = hash;
4187
+ });
4188
+ });
4189
+ });
4190
+ };
4191
+
3956
4192
  self.getClassMap = () => {
4193
+ resolveHashes();
3957
4194
  let classMap = {};
3958
4195
  let metaClass = {};
3959
4196
  Object.values(selectors).forEach(sel => {
@@ -3978,6 +4215,7 @@
3978
4215
  });
3979
4216
 
3980
4217
  Object.values(selectors).forEach(sel => {
4218
+ sel.$selector = true;
3981
4219
  if(sel.fullyGlobal || !sel.local) return;
3982
4220
  let selected;
3983
4221
  try {
@@ -3988,17 +4226,30 @@
3988
4226
  throw e;
3989
4227
  }
3990
4228
  selected.forEach(s => {
3991
- s.node.__node.classes.add(sel.local);
3992
- s.lvl.forEach(l => l.__node.classes.add(sel.local));
4229
+ s.node.__node.classes.add(sel);
4230
+ s.lvl.forEach(l => l.__node.classes.add(sel));
3993
4231
  });
3994
4232
  });
3995
4233
  };
3996
4234
 
4235
+ self.resolve = sel => {
4236
+ resolveHashes();
4237
+ assert(sel.resolved);
4238
+ if(sel.external) {
4239
+ assert(sel.external !== true);
4240
+ return sel.external;
4241
+ }
4242
+ assert(sel.local && sel.local !== true);
4243
+ return sel.local;
4244
+ };
4245
+
3997
4246
  self.getContent = function() {
3998
4247
  removeBlocks.forEach(node => {
3999
4248
  let i = node.parent.children.indexOf(node);
4000
4249
  if(i>=0) node.parent.children.splice(i, 1);
4001
4250
  });
4251
+ resolveHashes();
4252
+
4002
4253
  return astList.map(ast => csstree.generate(ast)).join('');
4003
4254
  };
4004
4255
  }
@@ -4024,10 +4275,8 @@
4024
4275
  //if(e.name[0].match(/[A-Z]/)) return;
4025
4276
  let n = new Node(e.name, {__node: e});
4026
4277
  e.attributes.forEach(a => {
4027
- if(a.name == 'class') {
4028
- if(a.value != null) n.className += ' ' + a.value;
4029
- n.attributes[a.name] = a.value;
4030
- } else if(a.name == 'id') n.attributes.id = n.id = a.value;
4278
+ if(a.name == 'class') n.className += ' ' + a.value;
4279
+ else if(a.name == 'id') n.id = a.value;
4031
4280
  else if(a.name.startsWith('class:')) {
4032
4281
  n.className += ' ' + a.name.substring(6);
4033
4282
  } else n.attributes[a.name] = a.value;
@@ -4069,10 +4318,6 @@
4069
4318
  return this.attributes[n];
4070
4319
  };
4071
4320
 
4072
- Node.prototype.hasAttribute = function(n) {
4073
- return n in this.attributes;
4074
- };
4075
-
4076
4321
  Node.prototype.appendChild = function(n) {
4077
4322
  n.parentElement = this;
4078
4323
  this.childNodes.push(n);
@@ -4111,22 +4356,21 @@
4111
4356
  return result;
4112
4357
  };
4113
4358
 
4114
- function makeComponent(node, element) {
4359
+ function makeComponent(node, requireCD) {
4360
+ this.require('apply');
4361
+
4115
4362
  let propList = node.attributes;
4116
- let forwardAllEvents = false;
4117
4363
 
4118
- this.require('$context', '$cd');
4364
+ this.require('$context');
4365
+ requireCD.$value(true); // FIX
4119
4366
 
4120
- let options = [];
4121
- let dynamicComponent;
4122
4367
  let reference = null;
4123
4368
  let propsFn = [], propsSetter = [], $class=[], staticProps = true;
4369
+ let slotBlocks = [];
4370
+ let anchorBlocks = [];
4124
4371
 
4125
4372
  let componentName = node.name;
4126
- if(componentName == 'component') {
4127
- assert(node.elArg);
4128
- dynamicComponent = node.elArg[0] == '{' ? unwrapExp(node.elArg) : node.elArg;
4129
- } else if(this.config.autoimport) {
4373
+ if(componentName != 'component' && this.config.autoimport) {
4130
4374
  let imported = this.script.autoimport[componentName] || this.script.importedNames.includes(componentName)
4131
4375
  || this.script.rootVariables[componentName] || this.script.rootFunctions[componentName];
4132
4376
 
@@ -4136,30 +4380,12 @@
4136
4380
  }
4137
4381
  }
4138
4382
 
4139
- let passOption = {};
4140
- let head = xNode('block');
4141
-
4142
- head.push(xNode('events', ctx => {
4143
- if(forwardAllEvents) {
4144
- this.require('$events');
4145
- ctx.writeLine('let events = {...$events};');
4146
- } else if(passOption.events) {
4147
- ctx.writeLine('let events = {};');
4148
- }
4149
- }));
4150
-
4151
- head.push(xNode('slots', ctx => {
4152
- if(passOption.slots) ctx.writeLine('let slots = {};');
4153
- }));
4154
-
4155
- head.push(xNode('anchor', ctx => {
4156
- if(passOption.anchor) ctx.writeLine('let anchor = {};');
4157
- }));
4158
-
4159
- let _boundEvents = {};
4160
- const boundEvent = (name) => {
4161
- if(!_boundEvents[name]) _boundEvents[name] = forwardAllEvents ? 1 : 0;
4162
- _boundEvents[name]++;
4383
+ // events
4384
+ let forwardAllEvents = false;
4385
+ let events = {};
4386
+ const passEvent = (name, bind) => {
4387
+ if(!events[name]) events[name] = [];
4388
+ events[name].push(bind);
4163
4389
  };
4164
4390
 
4165
4391
  if(node.body && node.body.length) {
@@ -4187,12 +4413,11 @@
4187
4413
  Object.values(slots).forEach(slot => {
4188
4414
  if(!slot.body.length) return;
4189
4415
  assert(isSimpleName(slot.name));
4190
- passOption.slots = true;
4191
4416
 
4192
4417
  let props;
4193
- let rx = slot.value && slot.value.match(/^#slot\S*\s+(.*)$/s);
4418
+ let rx = slot.value && slot.value.match(/^#slot\S*\s+(.*)$/);
4194
4419
  if(rx) {
4195
- props = rx[1].trim().split(/\s*,\s*/);
4420
+ props = rx[1].trim().split(/[\s,]+/);
4196
4421
  assert(props.length);
4197
4422
  props.forEach(n => {
4198
4423
  assert(isSimpleName(n), 'Wrong prop for slot');
@@ -4203,7 +4428,7 @@
4203
4428
  if(contentNodes.length == 1 && contentNodes[0].type == 'node' && contentNodes[0].name == 'slot') {
4204
4429
  let parentSlot = contentNodes[0];
4205
4430
  if(!parentSlot.body || !parentSlot.body.length) {
4206
- head.push(xNode('empty-slot', {
4431
+ slotBlocks.push(xNode('empty-slot', {
4207
4432
  childName: slot.name,
4208
4433
  parentName: parentSlot.elArg || 'default'
4209
4434
  }, (ctx, n) => {
@@ -4214,93 +4439,68 @@
4214
4439
  }
4215
4440
 
4216
4441
  if(props) this.require('apply');
4217
- this.require('$cd');
4442
+ requireCD.$value(true); // FIXME
4218
4443
 
4219
4444
  let block = this.buildBlock(slot, {inline: true});
4220
4445
 
4221
- const template = xNode('template', {
4222
- body: block.tpl,
4223
- svg: block.svg,
4224
- inline: true
4225
- });
4226
-
4227
- head.push(xNode('slot', {
4446
+ slotBlocks.push(xNode('slot', {
4447
+ $deps: [this.glob.apply],
4228
4448
  name: slot.name,
4229
- template,
4449
+ template: block.template,
4230
4450
  bind: block.source,
4231
4451
  componentName,
4232
4452
  props
4233
4453
  }, (ctx, n) => {
4234
4454
  if(n.bind) {
4235
- ctx.write(true, `slots.${n.name} = $runtime.makeSlot($cd, ($cd, $context, $instance_${n.componentName}`);
4236
- if(n.props) ctx.write(`, props`);
4237
- ctx.write(`) => {\n`);
4238
- ctx.goIndent(() => {
4239
- if(n.bind) {
4240
- let push = n.props && ctx.inuse.apply;
4241
- ctx.write(true, `let $parentElement = `);
4242
- ctx.build(n.template);
4243
- ctx.write(`;\n`);
4244
- if(n.props) {
4245
- ctx.writeLine(`let {${n.props.join(', ')}} = props;`);
4246
- if(push) ctx.writeLine(`let push = () => ({${n.props.join(', ')}} = props, $$apply());`);
4247
- }
4248
- ctx.build(n.bind);
4249
- if(push) ctx.writeLine(`return {push, el: $parentElement};`);
4250
- else ctx.writeLine(`return $parentElement;`);
4251
- } else {
4252
- ctx.write(true, `return `);
4253
- ctx.build(n.template);
4254
- ctx.write(`;\n`);
4255
- }
4256
- });
4257
- ctx.writeLine(`});`);
4455
+ ctx.write(true, `${n.name}: $runtime.makeSlot($cd, `);
4456
+ ctx.add(n.template);
4457
+ ctx.write(`, ($cd, $parentElement, $context, $instance_${n.componentName}`);
4458
+ if(n.props) ctx.write(`, $localProps`);
4459
+ ctx.write(`) => {`, true);
4460
+ ctx.indent++;
4461
+ if(n.props) ctx.write(true, `let {${n.props.join(', ')}} = $localProps;`);
4462
+ ctx.add(n.bind);
4463
+
4464
+ if(n.props && this.glob.apply.value) ctx.write(true, `return ($localProps) => ({${n.props.join(', ')}} = $localProps, $$apply());`);
4465
+ ctx.indent--;
4466
+ ctx.writeLine(`})`);
4258
4467
  } else {
4259
- ctx.write(true, `slots.${n.name} = $runtime.makeSlotStatic(() => `);
4260
- ctx.build(n.template);
4261
- ctx.write(`);\n`);
4468
+ ctx.write(true, `${n.name}: $runtime.makeStaticBlock(`);
4469
+ ctx.add(n.template);
4470
+ ctx.write(')');
4262
4471
  }
4263
4472
  }));
4264
4473
  });
4265
4474
 
4266
4475
  anchors.forEach(n => {
4267
- passOption.anchor = true;
4268
- let block = this.buildBlock({body: [n]}, {inline: true, oneElement: 'el', bindAttributes: true});
4476
+ let bb = this.buildBlock({body: [n]}, {inline: true, oneElement: 'el', bindAttributes: true});
4477
+ let block = bb.source;
4269
4478
  let name = n.name.slice(1) || 'default';
4270
4479
  assert(isSimpleName(name));
4271
- head.push(xNode('anchor', {
4480
+
4481
+ anchorBlocks.push(xNode('anchor', {
4482
+ $compile: [block],
4483
+ $deps: [bb.requireCD],
4272
4484
  name,
4273
- source: block.source,
4274
- $cd: block.inuse.$cd
4485
+ block
4275
4486
  }, (ctx, n) => {
4276
- ctx.writeLine(`anchor.${n.name} = (el) => {`);
4277
- ctx.goIndent(() => {
4278
- if(n.$cd) {
4279
- ctx.writeLine(`let $childCD = $cd.new();`);
4280
- ctx.writeLine(`{`);
4281
- ctx.goIndent(() => {
4282
- ctx.writeLine(`let $cd = $childCD;`);
4283
- ctx.build(n.source);
4284
- });
4285
- ctx.writeLine(`}`);
4286
- ctx.writeLine(`return () => {$childCD.destroy();}`);
4287
- } else {
4288
- ctx.build(n.source);
4289
- }
4290
- });
4291
- ctx.writeLine(`}`);
4487
+ let useCD = n.$deps[0].value;
4488
+ if(useCD) ctx.write(`${n.name}: {$: ($cd, el) => {`);
4489
+ else ctx.write(`${n.name}: (el) => {`);
4490
+ ctx.indent++;
4491
+ ctx.build(n.block);
4492
+ ctx.indent--;
4493
+ if(useCD) ctx.write(true, `}}`);
4494
+ else ctx.write(true, `}`);
4292
4495
  }));
4293
4496
  });
4294
4497
  }
4295
4498
 
4296
- propList = propList.filter(prop => {
4297
- let name = prop.name;
4298
- let value = prop.value;
4499
+ propList = propList.filter(({name}) => {
4299
4500
  if(name == '@@') {
4300
4501
  forwardAllEvents = true;
4301
4502
  return false;
4302
4503
  } else if(name == 'this') {
4303
- dynamicComponent = unwrapExp(value);
4304
4504
  return false;
4305
4505
  }
4306
4506
  return true;
@@ -4350,35 +4550,21 @@
4350
4550
  if(name.startsWith('@@')) {
4351
4551
  let event = name.substring(2);
4352
4552
  assert(!value);
4353
- passOption.events = true;
4354
- boundEvent(event);
4355
4553
  this.require('$events');
4356
- head.push(xNode('forwardEvent', {
4554
+ passEvent(event, xNode('forwardEvent', {
4357
4555
  event
4358
- }, (ctx, data) => {
4359
- if(_boundEvents[data.event] > 1) ctx.writeLine(`$runtime.$$addEventForComponent(events, '${data.event}', $events.${data.event});`);
4360
- else ctx.writeLine(`events.${data.event} = $events.${data.event};`);
4556
+ }, (ctx, n) => {
4557
+ ctx.write(`$events.${n.event}`);
4361
4558
  }));
4362
4559
  return;
4363
4560
  }
4364
4561
 
4365
4562
  let {event, fn} = this.makeEventProp(prop);
4366
4563
 
4367
- passOption.events = true;
4368
- boundEvent(event);
4369
- head.push(xNode('passEvent', {
4370
- event,
4564
+ passEvent(event, xNode('passEvent', {
4371
4565
  fn
4372
4566
  }, (ctx, n) => {
4373
- if(_boundEvents[n.event] > 1) {
4374
- ctx.write(true, `$runtime.$$addEventForComponent(events, '${n.event}', `);
4375
- ctx.build(n.fn);
4376
- ctx.write(`);\n`);
4377
- } else {
4378
- ctx.write(true, `events.${n.event} = `);
4379
- ctx.build(n.fn);
4380
- ctx.write(`;\n`);
4381
- }
4567
+ ctx.add(n.fn);
4382
4568
  }));
4383
4569
  return;
4384
4570
  } else if(this.config.passClass && (name == 'class' || name.startsWith('class:'))) {
@@ -4391,6 +4577,7 @@
4391
4577
  assert(metaClass);
4392
4578
  }
4393
4579
  assert(value);
4580
+ this.css.passingClass = true;
4394
4581
 
4395
4582
  const parsed = this.parseText(prop.value);
4396
4583
  this.detectDependency(parsed);
@@ -4408,41 +4595,87 @@
4408
4595
  });
4409
4596
 
4410
4597
 
4411
- if(forwardAllEvents || passOption.events) options.push('events');
4412
- if(passOption.slots) options.push('slots');
4413
- if(passOption.anchor) options.push('anchor');
4598
+ if(Object.keys(events).length == 0) events = null;
4414
4599
 
4415
4600
  let result = xNode('component', {
4416
- el: element.bindName(),
4417
4601
  componentName,
4418
- head,
4419
- options,
4420
- $cd: '$cd',
4421
4602
  staticProps,
4422
4603
  props: propsFn,
4423
4604
  propsSetter,
4424
4605
  reference,
4425
- $class
4606
+ $class,
4607
+ forwardAllEvents,
4608
+ events,
4609
+ slots: slotBlocks.length ? slotBlocks : null,
4610
+ anchors: anchorBlocks.length ? anchorBlocks : null
4426
4611
  }, (ctx, n) => {
4427
- const $cd = n.$cd || '$cd';
4428
-
4429
- let head = ctx.subBuild(n.head);
4430
- if(head) {
4431
- ctx.addBlock(head);
4432
- n.requireScope = true;
4433
- }
4612
+ if(n.reference) throw 'not implemented'; // FIXME
4613
+ let comma = false;
4614
+ ctx.write(`$runtime.callComponent($context, ${n.componentName}, {`);
4434
4615
 
4435
4616
  if(n.props.length && n.staticProps) {
4436
- n.options.push(`props: {${n.props.join(', ')}}`);
4617
+ ctx.write(`props: {${n.props.join(', ')}}`);
4618
+ comma = true;
4437
4619
  n.props = [];
4438
4620
  }
4439
-
4440
- ctx.write(true);
4441
- if(n.reference) ctx.write(`${n.reference} = `);
4442
- ctx.write(`$runtime.callComponent(${$cd}, $context, ${n.componentName}, ${n.el}, {${n.options.join(', ')}}`);
4621
+ ctx.indent++;
4622
+ if(n.forwardAllEvents && !n.events) {
4623
+ if(comma) ctx.write(', ');
4624
+ comma = true;
4625
+ ctx.write('events: $events');
4626
+ } else if(n.events && !n.forwardAllEvents) {
4627
+ if(comma) ctx.write(',', true);
4628
+ comma = true;
4629
+ ctx.write('events: {');
4630
+ ctx.indent++;
4631
+ ctx.write(true);
4632
+ Object.entries(n.events).forEach(([event, list], index) => {
4633
+ if(index) ctx.write(',', true);
4634
+ ctx.write(event + ': ');
4635
+ if(list.length == 1) ctx.add(list[0]);
4636
+ else {
4637
+ ctx.write('$runtime.mergeEvents(');
4638
+ list.forEach((b, i) => {
4639
+ if(i) ctx.write(', ');
4640
+ ctx.add(b);
4641
+ });
4642
+ ctx.write(')');
4643
+ }
4644
+ });
4645
+ ctx.indent--;
4646
+ ctx.write(true, '}');
4647
+ } else if(n.events && n.forwardAllEvents) {
4648
+ throw 'not implemented'; // FIXME
4649
+ }
4650
+ if(n.slots) {
4651
+ if(comma) ctx.write(', ');
4652
+ comma = true;
4653
+ ctx.write('slots: {');
4654
+ ctx.indent++;
4655
+ n.slots.forEach((slot, i) => {
4656
+ if(i) ctx.write(',');
4657
+ ctx.write(true);
4658
+ ctx.add(slot);
4659
+ });
4660
+ ctx.indent--;
4661
+ ctx.write(true, '}');
4662
+ }
4663
+ if(n.anchors) {
4664
+ if(comma) ctx.write(', ');
4665
+ comma = true;
4666
+ ctx.write('anchor: {');
4667
+ ctx.indent++;
4668
+ n.anchors.forEach((anchor, i) => {
4669
+ if(i) ctx.write(',');
4670
+ ctx.write(true);
4671
+ ctx.add(anchor);
4672
+ });
4673
+ ctx.indent--;
4674
+ ctx.write(true, '}');
4675
+ }
4676
+ ctx.write('}');
4443
4677
 
4444
4678
  let other = '';
4445
- ctx.indent++;
4446
4679
  if(n.props.length) ctx.write(`,\n`, true, `() => ({${n.props.join(', ')}})`);
4447
4680
  else other = ', null';
4448
4681
 
@@ -4468,77 +4701,61 @@
4468
4701
  } else other += ', null';
4469
4702
 
4470
4703
  ctx.indent--;
4471
- ctx.write(`);\n`);
4704
+ ctx.write(true, `)`);
4472
4705
  });
4473
4706
 
4474
- if(!dynamicComponent) {
4475
- return {bind: xNode('component-scope', {
4476
- component: result
4477
- }, (ctx, n) => {
4478
- let r = ctx.subBuild(n.component);
4479
-
4480
- if(n.component.requireScope) {
4481
- ctx.writeLine('{');
4482
- ctx.goIndent(() => {
4483
- ctx.addBlock(r);
4484
- });
4485
- ctx.writeLine('}');
4486
- } else ctx.addBlock(r);
4487
- })};
4488
- } else {
4489
- this.detectDependency(dynamicComponent);
4707
+ return {bind: result};
4708
+ }
4709
+ function makeComponentDyn(node, requireCD, element) {
4710
+ let dynamicComponent;
4490
4711
 
4491
- result.componentName = '$ComponentConstructor';
4492
- result.$cd = 'childCD';
4493
- return {bind: xNode('dyn-component', {
4494
- el: element.bindName(),
4495
- exp: dynamicComponent,
4496
- component: result
4497
- }, (ctx, n) => {
4498
- ctx.writeLine('{');
4499
- ctx.goIndent(() => {
4500
- if(ctx.inuse.apply) {
4501
- ctx.writeLine(`let childCD, finalLabel = $runtime.getFinalLabel(${n.el});`);
4502
- ctx.writeLine(`$watch($cd, () => (${n.exp}), ($ComponentConstructor) => {`);
4503
- ctx.goIndent(() => {
4504
- ctx.writeLine(`if(childCD) {`);
4505
- ctx.goIndent(() => {
4506
- ctx.writeLine(`childCD.destroy();`);
4507
- ctx.writeLine(`$runtime.removeElementsBetween(${n.el}, finalLabel);`);
4508
- });
4509
- ctx.writeLine(`}`);
4510
- ctx.writeLine(`childCD = null;`);
4511
- ctx.writeLine(`if($ComponentConstructor) {`);
4512
- ctx.goIndent(() => {
4513
- ctx.writeLine(`childCD = $cd.new();`);
4514
- ctx.build(n.component);
4515
- });
4516
- ctx.writeLine(`}`);
4517
- });
4518
- ctx.writeLine(`});`);
4519
- } else {
4520
- ctx.writeLine(`let $ComponentConstructor = ${n.exp};`);
4521
- ctx.writeLine(`if($ComponentConstructor) {`);
4522
- ctx.goIndent(() => {
4523
- ctx.writeLine(`let childCD = $cd;`);
4524
- ctx.build(n.component);
4525
- });
4526
- ctx.writeLine(`}`);
4527
- }
4528
- });
4529
- ctx.writeLine('}');
4530
- })};
4712
+ if(node.elArg) {
4713
+ dynamicComponent = node.elArg[0] == '{' ? unwrapExp(node.elArg) : node.elArg;
4714
+ } else {
4715
+ node.props.some(({name, value}) => {
4716
+ if(name == 'this') {
4717
+ dynamicComponent = unwrapExp(value);
4718
+ return true;
4719
+ }
4720
+ });
4531
4721
  }
4722
+
4723
+ assert(dynamicComponent);
4724
+ this.detectDependency(dynamicComponent);
4725
+ requireCD.$value(true);
4726
+
4727
+ let component = this.makeComponent(node, requireCD).bind;
4728
+
4729
+ component.componentName = '$ComponentConstructor';
4730
+ return xNode('dyn-component', {
4731
+ el: element.bindName(),
4732
+ exp: dynamicComponent,
4733
+ component
4734
+ }, (ctx, n) => {
4735
+ ctx.write(true, `$runtime.attachDynComponent($cd, ${n.el}, () => ${n.exp}, ($ComponentConstructor) => `);
4736
+ ctx.add(n.component);
4737
+ ctx.write(')');
4738
+ });
4532
4739
  }
4533
4740
 
4534
- function bindProp(prop, node, element) {
4741
+ function bindProp(prop, node, element, requireCD) {
4535
4742
  let name, arg;
4743
+
4744
+ if(prop.content.startsWith('{*')) {
4745
+ const pe = this.parseText(prop.content);
4746
+ assert(pe.parts[0].type == 'js');
4747
+ let exp = pe.parts[0].value;
4748
+ if(!exp.endsWith(';')) exp += ';';
4749
+ return {bind: xNode('block', {body: [
4750
+ replaceElementKeyword(exp, () => element.bindName())
4751
+ ]})};
4752
+ }
4753
+
4536
4754
  if(prop.name[0] == '@' || prop.name.startsWith('on:')) name = 'event';
4537
- if(!name && prop.name[0] == ':') {
4755
+ else if(prop.name[0] == ':') {
4538
4756
  name = 'bind';
4539
4757
  arg = prop.name.substring(1);
4540
- }
4541
- if(!name && prop.name[0] == '*') {
4758
+ } else if(prop.name[0] == '*') {
4542
4759
  let rx = prop.name.match(/^\*\{.*\}$/);
4543
4760
  if(rx) {
4544
4761
  assert(prop.value == null, 'wrong binding: ' + prop.content);
@@ -4548,17 +4765,7 @@
4548
4765
  name = 'use';
4549
4766
  arg = prop.name.substring(1);
4550
4767
  }
4551
- }
4552
- if(prop.content.startsWith('{*')) {
4553
- const pe = this.parseText(prop.content);
4554
- assert(pe.parts[0].type == 'js');
4555
- let exp = pe.parts[0].value;
4556
- if(!exp.endsWith(';')) exp += ';';
4557
- return {bind: xNode('block', {body: [
4558
- replaceElementKeyword(exp, () => element.bindName())
4559
- ]})};
4560
- }
4561
- if(!name && prop.value == null) {
4768
+ } else if(prop.value == null) {
4562
4769
  let rx = prop.name.match(/^\{(.*)\}$/);
4563
4770
  if(rx) {
4564
4771
  name = rx[1];
@@ -4566,7 +4773,6 @@
4566
4773
  // spread operator
4567
4774
  name = name.substring(3);
4568
4775
  assert(detectExpressionType(name) == 'identifier');
4569
- this.detectDependency(name);
4570
4776
  return node.spreading.push(`...${name}`);
4571
4777
  } else {
4572
4778
  prop.value = prop.name;
@@ -4597,7 +4803,8 @@
4597
4803
  } else if(name == 'event') {
4598
4804
  if(prop.name.startsWith('@@')) {
4599
4805
  assert(!prop.value);
4600
- this.require('$cd', '$events');
4806
+ requireCD.$value(true);
4807
+ this.require('$events');
4601
4808
  if(prop.name == '@@') {
4602
4809
  return {bind: xNode('forwardAllEvents', {
4603
4810
  el: element.bindName()
@@ -4617,18 +4824,20 @@
4617
4824
  })};
4618
4825
  }
4619
4826
 
4620
- let {event, fn} = this.makeEventProp(prop, () => element.bindName());
4621
-
4622
- this.require('$cd');
4827
+ let {event, fn, rootModifier} = this.makeEventProp(prop, () => element.bindName());
4828
+ if(rootModifier) this.require('rootEvent');
4829
+ else requireCD.$value(true);
4623
4830
 
4624
4831
  return {bind: xNode('bindEvent', {
4625
4832
  event,
4626
4833
  fn,
4627
- el: element.bindName()
4834
+ el: element.bindName(),
4835
+ rootModifier
4628
4836
  }, (ctx, n) => {
4629
- ctx.write(true, `$runtime.addEvent($cd, ${n.el}, '${n.event}', `);
4837
+ if(n.rootModifier) ctx.write(true, `$$addRootEvent(${n.el}, '${n.event}', `);
4838
+ else ctx.write(true, `$runtime.addEvent($cd, ${n.el}, '${n.event}', `);
4630
4839
  ctx.build(n.fn);
4631
- ctx.write(`);\n`);
4840
+ ctx.write(`);`);
4632
4841
  })};
4633
4842
  } else if(name == 'bind') {
4634
4843
  if(this.script.readOnly) {
@@ -4636,7 +4845,8 @@
4636
4845
  return;
4637
4846
  }
4638
4847
 
4639
- this.require('apply', '$cd');
4848
+ requireCD.$value(true);
4849
+ this.require('apply');
4640
4850
  let exp;
4641
4851
  arg = arg.split(/[\:\|]/);
4642
4852
  let attr = arg.shift();
@@ -4715,7 +4925,7 @@
4715
4925
  })};
4716
4926
  } else if(name == 'use') {
4717
4927
  if(arg) {
4718
- this.require('$cd');
4928
+ requireCD.$value(true);
4719
4929
  assert(isSimpleName(arg), 'Wrong name: ' + arg);
4720
4930
  this.checkRootName(arg);
4721
4931
  let args = prop.value ? `, () => [${getExpression()}]` : '';
@@ -4779,8 +4989,7 @@
4779
4989
  });
4780
4990
 
4781
4991
  if(compound) {
4782
- let defaultHash = '';
4783
- if(node.classes.has(this.css.id)) defaultHash = this.css.id;
4992
+ let classes = Array.from(node.classes);
4784
4993
  node.classes.clear();
4785
4994
  if(this.config.passClass) this.require('resolveClass');
4786
4995
  let exp = props.map(prop => {
@@ -4797,24 +5006,40 @@
4797
5006
  const bind = xNode('compound-class', {
4798
5007
  el: element.bindName(),
4799
5008
  exp,
4800
- defaultHash
5009
+ classes,
5010
+ requireCD
4801
5011
  }, (ctx, n) => {
5012
+ let base = '';
5013
+ if(n.classes.length) {
5014
+ if(this.css.passingClass) {
5015
+ base = [];
5016
+ n.classes.forEach(c => {
5017
+ if(c.local) base.push(this.css.resolve(c));
5018
+ });
5019
+ base = base.join(' ');
5020
+ if(base) base = `, '${base}'`;
5021
+ } else {
5022
+ if(n.classes.some(c => c.local)) base = `,'${this.css.id}'`;
5023
+ }
5024
+ }
5025
+
4802
5026
  if(ctx.inuse.resolveClass) {
4803
- let base = n.defaultHash ? `,'${n.defaultHash}'` : '';
4804
5027
  if(ctx.inuse.apply) {
4805
- ctx.writeLine(`$watchReadOnly($cd, () => $$resolveClass((${n.exp})${base}), value => $runtime.setClassToElement(${n.el}, value));`);
5028
+ n.requireCD.$value(true);
5029
+ ctx.write(true, `$runtime.bindClassExp($cd, ${n.el}, () => $$resolveClass((${n.exp})${base}))`);
4806
5030
  } else {
4807
- ctx.writeLine(`$runtime.setClassToElement(${n.el}, $$resolveClass((${n.exp})${base}));`);
5031
+ ctx.write(true, `$runtime.setClassToElement(${n.el}, $$resolveClass((${n.exp})${base}));`);
4808
5032
  }
4809
5033
  } else {
4810
- let base = n.defaultHash ? ` + ' ${n.defaultHash}'` : '';
4811
5034
  if(ctx.inuse.apply) {
4812
- ctx.writeLine(`$watchReadOnly($cd, () => ${n.exp}${base}, value => $runtime.setClassToElement(${n.el}, value));`);
5035
+ n.requireCD.$value(true);
5036
+ ctx.write(true, `$runtime.bindClassExp($cd, ${n.el}, () => (${n.exp})${base})`);
4813
5037
  } else {
4814
- ctx.writeLine(`$runtime.setClassToElement(${n.el}, ${n.exp}${base});`);
5038
+ ctx.write(true, `$runtime.setClassToElement(${n.el}, ${n.exp}${base});`);
4815
5039
  }
4816
5040
  }
4817
5041
  });
5042
+ requireCD.$depends(bind);
4818
5043
  return {bind};
4819
5044
  } else {
4820
5045
  let bind = xNode('block');
@@ -4828,18 +5053,22 @@
4828
5053
  assert(className);
4829
5054
  let exp = prop.value ? unwrapExp(prop.value) : className;
4830
5055
  this.detectDependency(exp);
4831
- bind.push(xNode('bindClass', {
5056
+
5057
+ let n = xNode('bindClass', {
5058
+ $deps: [this.glob.apply],
4832
5059
  el: element.bindName(),
4833
5060
  className,
4834
5061
  exp,
4835
- $element: exp.includes('$element')
5062
+ $element: exp.includes('$element'),
5063
+ requireCD
4836
5064
  }, (ctx, n) => {
4837
5065
  if(n.$element) {
4838
5066
  ctx.writeLine(`{`);
4839
5067
  ctx.indent++;
4840
5068
  ctx.writeLine(`let $element = ${n.el};`);
4841
5069
  }
4842
- if(ctx.inuse.apply) {
5070
+ if(this.glob.apply.value) {
5071
+ n.requireCD.$value(true);
4843
5072
  ctx.writeLine(`$runtime.bindClass($cd, ${n.el}, () => !!(${n.exp}), '${n.className}');`);
4844
5073
  } else {
4845
5074
  ctx.writeLine(`(${n.exp}) && $runtime.addClass(${n.el}, '${n.className}');`);
@@ -4848,18 +5077,22 @@
4848
5077
  ctx.indent--;
4849
5078
  ctx.writeLine(`}`);
4850
5079
  }
4851
- }));
5080
+ });
5081
+ requireCD.$depends(n);
5082
+ bind.push(n);
4852
5083
  }
4853
5084
  });
4854
5085
  return {bind: bind.body.length ? bind : null};
4855
5086
  }
4856
5087
  } else if(name[0] == '^') {
4857
- this.require('$cd');
5088
+ requireCD.$value(true);
4858
5089
  return {bindTail: xNode('bindAnchor', {
4859
5090
  name: name.slice(1) || 'default',
4860
5091
  el: element.bindName()
4861
5092
  }, (ctx, n) => {
4862
- ctx.writeLine(`$runtime.attachAnchor($option, $cd, '${n.name}', ${n.el});`);
5093
+ ctx.write(true, `$runtime.attachAnchor($option, $cd, ${n.el}`);
5094
+ if(n.name == 'default') ctx.write(`);`);
5095
+ else ctx.write(`, '${n.name}');`);
4863
5096
  })};
4864
5097
  } else {
4865
5098
  if(prop.value && prop.value.indexOf('{') >= 0) {
@@ -4878,36 +5111,41 @@
4878
5111
  selected: true,
4879
5112
  innerHTML: true,
4880
5113
  innerText: true,
5114
+ placeholder: true,
4881
5115
  src: true,
4882
5116
  readonly: 'readOnly'
4883
5117
  };
4884
5118
 
5119
+ let n = xNode('bindAttribute', {
5120
+ name,
5121
+ exp,
5122
+ hasElement,
5123
+ el: element.bindName(),
5124
+ requireCD
5125
+ }, (ctx, data) => {
5126
+ if(data.hasElement) ctx.writeLine(`let $element=${data.el};`);
5127
+ if(propList[name]) {
5128
+ let propName = propList[name] === true ? name : propList[name];
5129
+ if(ctx.inuse.apply) {
5130
+ requireCD.$value(true);
5131
+ ctx.writeLine(`$watchReadOnly($cd, () => (${data.exp}), (value) => {${data.el}.${propName} = value;});`);
5132
+ } else {
5133
+ ctx.writeLine(`${data.el}.${propName} = ${data.exp};`);
5134
+ }
5135
+ } else {
5136
+ if(ctx.inuse.apply) {
5137
+ requireCD.$value(true);
5138
+ ctx.writeLine(`$runtime.bindAttribute($cd, ${data.el}, '${data.name}', () => (${data.exp}));`);
5139
+ } else {
5140
+ ctx.writeLine(`$runtime.bindAttributeBase(${data.el}, '${data.name}', ${data.exp});`);
5141
+ }
5142
+ }
5143
+ });
5144
+ requireCD.$depends(n);
5145
+
4885
5146
  return {bind: xNode('block', {
4886
5147
  scope: hasElement,
4887
- body: [
4888
- xNode('bindAttribute', {
4889
- name,
4890
- exp,
4891
- hasElement,
4892
- el: element.bindName()
4893
- }, (ctx, data) => {
4894
- if(data.hasElement) ctx.writeLine(`let $element=${data.el};`);
4895
- if(propList[name]) {
4896
- let propName = propList[name] === true ? name : propList[name];
4897
- if(ctx.inuse.apply) {
4898
- ctx.writeLine(`$watchReadOnly($cd, () => (${data.exp}), (value) => {${data.el}.${propName} = value;});`);
4899
- } else {
4900
- ctx.writeLine(`${data.el}.${propName} = ${data.exp};`);
4901
- }
4902
- } else {
4903
- if(ctx.inuse.apply) {
4904
- ctx.writeLine(`$runtime.bindAttribute($cd, ${data.el}, '${data.name}', () => (${data.exp}));`);
4905
- } else {
4906
- ctx.writeLine(`$runtime.bindAttributeBase(${data.el}, '${data.name}', ${data.exp});`);
4907
- }
4908
- }
4909
- })
4910
- ]
5148
+ body: [n]
4911
5149
  })};
4912
5150
  }
4913
5151
 
@@ -4920,104 +5158,86 @@
4920
5158
  }
4921
5159
  }
4922
5160
 
4923
- function makeifBlock(data, element) {
4924
- let r = data.value.match(/^#if (.*)$/s);
5161
+ function makeifBlock(data, element, requireCD) {
5162
+ let r = data.value.match(/^#if (.*)$/);
4925
5163
  let exp = r[1];
4926
5164
  assert(exp, 'Wrong binding: ' + data.value);
4927
5165
  this.detectDependency(exp);
4928
5166
  this.require('$cd');
4929
5167
 
4930
- let mainBlock, elseBlock, mainTpl, elseTpl;
5168
+ let mainBlock, elseBlock;
4931
5169
 
4932
- if(data.bodyMain) {
4933
- mainBlock = this.buildBlock({body: data.bodyMain}, {protectLastTag: true, inline: true});
4934
- elseBlock = this.buildBlock(data, {protectLastTag: true, inline: true});
5170
+ const getBlock = b => {
5171
+ if(b.singleBlock) {
5172
+ return xNode('make-block', {
5173
+ block: b.singleBlock
5174
+ }, (ctx, n) => {
5175
+ ctx.write('() => ');
5176
+ ctx.add(n.block);
5177
+ })
5178
+ }
5179
+ return b.block;
5180
+ };
4935
5181
 
4936
- elseTpl = xNode('template', {
4937
- inline: true,
4938
- body: elseBlock.tpl,
4939
- svg: elseBlock.svg
4940
- });
5182
+ if(data.bodyMain) {
5183
+ mainBlock = getBlock(this.buildBlock({body: data.bodyMain}, {protectLastTag: true, allowSingleBlock: true}));
5184
+ elseBlock = getBlock(this.buildBlock(data, {protectLastTag: true, allowSingleBlock: true}));
4941
5185
  } else {
4942
- mainBlock = this.buildBlock(data, {protectLastTag: true, inline: true});
5186
+ mainBlock = getBlock(this.buildBlock(data, {protectLastTag: true, allowSingleBlock: true}));
4943
5187
  }
5188
+
5189
+ const result = xNode('if:bind', {
5190
+ $deps: [this.glob.apply],
5191
+ requireCD,
5192
+ el: element.bindName(),
5193
+ exp,
5194
+ mainBlock: mainBlock,
5195
+ elseBlock: elseBlock
5196
+ }, (ctx, n) => {
5197
+ if(this.glob.apply.value) {
5198
+ n.requireCD.$value(true);
5199
+ ctx.write(true, `$runtime.ifBlock($cd, ${n.el}, () => !!(${n.exp}),`);
5200
+ } else {
5201
+ this.glob.component.$value(true);
5202
+ ctx.write(true, `$runtime.ifBlockReadOnly($component, ${n.el}, () => !!(${n.exp}),`);
5203
+ }
4944
5204
 
4945
- mainTpl = xNode('template', {
4946
- inline: true,
4947
- body: mainBlock.tpl,
4948
- svg: mainBlock.svg
4949
- });
4950
-
4951
- const source = xNode('if:bind', {
4952
- el: element.bindName(),
4953
- exp,
4954
- mainTpl,
4955
- mainBlock: mainBlock.source,
4956
- elseTpl,
4957
- elseBlock: elseBlock && elseBlock.source
4958
- },
4959
- (ctx, data) => {
4960
- ctx.writeLine(`$runtime.$$ifBlock($cd, ${data.el}, () => !!(${data.exp}),`);
4961
5205
  ctx.indent++;
4962
- ctx.writeIndent();
4963
- ctx.build(data.mainTpl);
4964
- ctx.write(',\n');
4965
- ctx.writeIndent();
4966
- if(data.mainBlock) {
4967
- ctx.build(xNode('function', {
4968
- inline: true,
4969
- arrow: true,
4970
- args: ['$cd', '$parentElement'],
4971
- body: [data.mainBlock]
4972
- }));
4973
- } else ctx.write('$runtime.noop');
4974
- if(data.elseTpl) {
4975
- ctx.write(',\n');
4976
- ctx.writeIndent();
4977
- ctx.build(data.elseTpl);
4978
- ctx.write(',\n');
4979
- ctx.writeIndent();
4980
- if(data.elseBlock) {
4981
- ctx.build(xNode('function', {
4982
- inline: true,
4983
- arrow: true,
4984
- args: ['$cd', '$parentElement'],
4985
- body: [data.elseBlock]
4986
- }));
4987
- } else ctx.write('$runtime.noop');
5206
+ ctx.write(true);
5207
+ ctx.add(n.mainBlock);
5208
+ if(n.elseBlock) {
5209
+ ctx.write(',');
5210
+ ctx.add(n.elseBlock);
4988
5211
  }
4989
5212
  ctx.indent--;
4990
- ctx.write(');\n');
5213
+ ctx.write(true, ');', true);
4991
5214
  });
4992
-
4993
- return {source};
5215
+ requireCD.$depends(result);
5216
+ this.glob.component.$depends(result);
5217
+ return result;
4994
5218
  }
4995
5219
 
4996
5220
  function makeEachBlock(data, option) {
4997
-
4998
- let nodeItems = trimEmptyNodes(data.body);
4999
- if(!nodeItems.length) nodeItems = [data.body[0]];
5000
-
5001
- let itemData = this.buildBlock({body: nodeItems}, {protectLastTag: true, inline: true});
5221
+ this.require('apply');
5002
5222
 
5003
5223
  // #each items as item, index (key)
5004
- let rx = data.value.match(/^#each\s+(.+)\s+as\s+(.+)$/s);
5224
+ let rx = data.value.match(/^#each\s+(.+)\s+as\s+(.+)$/);
5005
5225
  assert(rx, `Wrong #each expression '${data.value}'`);
5006
5226
  let arrayName = rx[1];
5007
5227
  let right = rx[2];
5008
5228
  let keyName;
5009
5229
 
5010
5230
  // get keyName
5011
- rx = right.match(/^(.*)\s*\(\s*([^\(\)]+)\s*\)\s*$/s);
5231
+ rx = right.match(/^(.*)\s*\(\s*([^\(\)]+)\s*\)\s*$/);
5012
5232
  if(rx) {
5013
5233
  right = rx[1];
5014
5234
  keyName = rx[2];
5015
5235
  }
5016
5236
  right = right.trim();
5017
5237
 
5018
- let itemName, indexName, bind0 = null;
5238
+ let itemName, indexName, blockPrefix = null;
5019
5239
  if(right[0] == '{') {
5020
- rx = right.match(/^(\{[^}]+\})(.*)$/s);
5240
+ rx = right.match(/^(\{[^}]+\})(.*)$/);
5021
5241
  assert(rx, `Wrong #each expression '${data.value}'`);
5022
5242
  let exp = rx[1], keywords;
5023
5243
 
@@ -5032,12 +5252,15 @@
5032
5252
  if(indexName[0] == ',') indexName = indexName.substring(1).trim();
5033
5253
  indexName = indexName || '$index';
5034
5254
 
5035
- bind0 = xNode('each:unwrap', {
5255
+ blockPrefix = xNode('each:unwrap', {
5036
5256
  exp,
5037
5257
  keywords
5038
5258
  }, (ctx, n) => {
5039
- ctx.writeLine(`let ${n.keywords.join(', ')};`);
5040
- ctx.writeLine(`$runtime.prefixPush($ctx.cd, () => (${n.exp} = $$item));`);
5259
+ if(this.script.readOnly) ctx.writeLine(`let ${n.exp} = $$item;`);
5260
+ else {
5261
+ ctx.writeLine(`let ${n.keywords.join(', ')};`);
5262
+ ctx.writeLine(`$runtime.prefixPush($cd, () => (${n.exp} = $$item));`);
5263
+ }
5041
5264
  });
5042
5265
  } else {
5043
5266
  rx = right.trim().split(/\s*\,\s*/);
@@ -5070,82 +5293,93 @@
5070
5293
  })]
5071
5294
  });
5072
5295
  }
5073
- let bind;
5074
- if(itemData.source) {
5075
- bind = xNode('function', {
5076
- inline: true,
5077
- arrow: true,
5078
- args: ['$ctx', '$parentElement', itemName, indexName],
5079
- body: [
5080
- `let $cd = $ctx.cd;`,
5081
- bind0,
5082
- itemData.source,
5083
- xNode(ctx => {
5084
- ctx.writeLine(`$ctx.rebind = function(_${indexName}, _${itemName}) {`);
5085
- ctx.indent++;
5086
- ctx.writeLine(`${indexName} = _${indexName};`);
5087
- ctx.writeLine(`${itemName} = _${itemName};`);
5088
- ctx.indent--;
5089
- ctx.writeLine(`};`);
5090
- })
5091
- ]
5092
- });
5093
- } else {
5094
- bind = xNode('function', {
5095
- inline: true,
5096
- arrow: true,
5097
- args: ['$ctx'],
5098
- body: [`$ctx.rebind = $runtime.noop;`]
5296
+ let rebind;
5297
+ if(!this.script.readOnly) {
5298
+ rebind = xNode('block', {
5299
+ itemName,
5300
+ indexName
5301
+ }, (ctx, n) => {
5302
+ ctx.write(`(_${n.indexName}, _${n.itemName}) => {${n.indexName}=_${n.indexName}; ${n.itemName}=_${n.itemName};}`);
5099
5303
  });
5100
5304
  }
5101
5305
 
5102
- const template = xNode('template', {
5103
- inline: true,
5104
- body: itemData.tpl,
5105
- svg: itemData.svg
5306
+ let nodeItems = trimEmptyNodes(data.body);
5307
+ if(!nodeItems.length) nodeItems = [data.body[0]];
5308
+
5309
+ let itemBlock, block = this.buildBlock({body: nodeItems}, {
5310
+ protectLastTag: true,
5311
+ allowSingleBlock: !blockPrefix,
5312
+ each: {
5313
+ blockPrefix,
5314
+ rebind,
5315
+ itemName,
5316
+ indexName
5317
+ }
5106
5318
  });
5107
5319
 
5108
- this.require('$cd');
5320
+ if(block.singleBlock) {
5321
+ itemBlock = xNode('each-component', {
5322
+ block: block.singleBlock,
5323
+ rebind,
5324
+ itemName,
5325
+ indexName
5326
+ }, (ctx, n) => {
5327
+ ctx.write(`$runtime.makeEachSingleBlock((${n.itemName}, ${n.indexName}) => [`);
5328
+ ctx.indent++;
5329
+ ctx.write(true);
5330
+ ctx.add(n.rebind);
5331
+ ctx.write(',', true);
5332
+ ctx.add(n.block);
5333
+ ctx.indent--;
5334
+ ctx.write(true, `])`);
5335
+ });
5336
+ } else itemBlock = block.block;
5337
+
5109
5338
  const source = xNode('each', {
5110
5339
  keyFunction,
5111
- template,
5112
- bind
5113
- }, (ctx, data) => {
5340
+ block: itemBlock,
5341
+ }, (ctx, n) => {
5114
5342
  ctx.writeLine(`$runtime.$$eachBlock($cd, ${option.elName}, ${option.onlyChild?1:0}, () => (${arrayName}),`);
5115
5343
  ctx.indent++;
5116
- ctx.writeIndent();
5117
- if(data.keyFunction === 'noop') ctx.write('$runtime.noop');
5118
- else if(data.keyFunction) ctx.build(data.keyFunction);
5344
+ ctx.write(true);
5345
+ if(n.keyFunction === 'noop') ctx.write('$runtime.noop');
5346
+ else if(n.keyFunction) ctx.add(n.keyFunction);
5119
5347
  else ctx.write('$runtime.eachDefaultKey');
5120
- ctx.write(`,\n`);
5121
- ctx.writeIndent();
5122
- ctx.build(data.template);
5123
- ctx.write(`,\n`);
5124
- ctx.writeIndent();
5125
- ctx.build(data.bind);
5126
- ctx.write(`);\n`);
5348
+ ctx.write(`,`);
5349
+ ctx.add(n.block);
5127
5350
  ctx.indent--;
5351
+ ctx.write(true, `);`, true);
5128
5352
  });
5129
5353
  this.detectDependency(arrayName);
5130
5354
 
5131
5355
  return {source};
5132
5356
  }
5133
5357
 
5134
- function makeHtmlBlock(exp, label) {
5358
+ function makeHtmlBlock(exp, label, requireCD) {
5135
5359
  this.detectDependency(exp);
5136
5360
  this.require('$cd');
5137
- return xNode('block', {
5361
+ const result = xNode('block', {
5362
+ $deps: [this.glob.apply],
5138
5363
  el: label.bindName(),
5139
- exp
5364
+ exp,
5365
+ requireCD
5140
5366
  }, (ctx, n) => {
5141
- ctx.writeLine(`$runtime.$$htmlBlock($cd, ${n.el}, () => (${n.exp}));\n`);
5367
+ let cd;
5368
+ if(this.glob.apply.value) {
5369
+ n.requireCD.$value(true);
5370
+ cd = '$cd';
5371
+ } else cd = 'null';
5372
+ ctx.write(true, `$runtime.$$htmlBlock(${cd}, ${n.el}, () => (${n.exp}));`);
5142
5373
  });
5374
+
5375
+ requireCD.$depends(result);
5376
+ return result;
5143
5377
  }
5144
5378
 
5145
5379
  function makeAwaitBlock(node, element) {
5146
5380
  let valueForThen, exp;
5147
5381
 
5148
- let rx = node.value.match(/^#await\s+(.+)\s+then\s+(\S+)\s*$/s);
5382
+ let rx = node.value.match(/^#await\s+(.+)\s+then\s+(\S+)\s*$/);
5149
5383
  if(rx) {
5150
5384
  assert(!node.parts.then);
5151
5385
  node.parts.then = node.parts.main;
@@ -5153,7 +5387,7 @@
5153
5387
  exp = rx[1];
5154
5388
  valueForThen = rx[2];
5155
5389
  } else {
5156
- rx = node.value.match(/^#await\s+(.+)\s*$/s);
5390
+ rx = node.value.match(/^#await\s+(.+)\s*$/);
5157
5391
  assert(rx);
5158
5392
  exp = rx[1].trim();
5159
5393
  }
@@ -5162,7 +5396,7 @@
5162
5396
 
5163
5397
  let parts = [null, null, null];
5164
5398
  if(node.parts.main && node.parts.main.length) {
5165
- parts[0] = this.buildBlock({body: node.parts.main}, {protectLastTag: true, inlineFunction: true});
5399
+ parts[0] = this.buildBlock({body: node.parts.main}, {protectLastTag: true});
5166
5400
  }
5167
5401
  if(node.parts.then && node.parts.then.length) {
5168
5402
  let args = [];
@@ -5170,27 +5404,30 @@
5170
5404
  assert(isSimpleName(valueForThen));
5171
5405
  args.push(valueForThen);
5172
5406
  } else {
5173
- let rx = node.parts.thenValue.match(/^[^ ]+\s+(.*)$/s);
5407
+ let rx = node.parts.thenValue.match(/^[^ ]+\s+(.*)$/);
5174
5408
  if(rx) {
5175
5409
  assert(isSimpleName(rx[1]));
5176
5410
  args.push(rx[1]);
5177
5411
  }
5178
5412
  }
5179
- parts[1] = this.buildBlock({body: node.parts.then}, {protectLastTag: true, inlineFunction: true, args});
5413
+ parts[1] = this.buildBlock({body: node.parts.then}, {protectLastTag: true, extraArguments: args});
5180
5414
  }
5181
5415
  if(node.parts.catch && node.parts.catch.length) {
5182
5416
  let args = [];
5183
- let rx = node.parts.catchValue.match(/^[^ ]+\s+(.*)$/s);
5417
+ let rx = node.parts.catchValue.match(/^[^ ]+\s+(.*)$/);
5184
5418
  if(rx) {
5185
5419
  assert(isSimpleName(rx[1]));
5186
5420
  args.push(rx[1]);
5187
5421
  }
5188
- parts[2] = this.buildBlock({body: node.parts.catch}, {protectLastTag: true, inlineFunction: true, args});
5422
+ parts[2] = this.buildBlock({body: node.parts.catch}, {protectLastTag: true, extraArguments: args});
5189
5423
  }
5190
5424
 
5425
+ if(this.script.readOnly) {
5426
+ this.warning('script read-only conflicts with await');
5427
+ return;
5428
+ }
5191
5429
  this.detectDependency(exp);
5192
- if(this.script.readOnly) this.warning('script read-only conflicts with await');
5193
- this.require('apply', '$cd');
5430
+ this.require('apply');
5194
5431
 
5195
5432
  return xNode('await', {
5196
5433
  el: element.bindName(),
@@ -5198,127 +5435,88 @@
5198
5435
  parts,
5199
5436
  keywords
5200
5437
  }, (ctx, n) => {
5201
- ctx.writeIndent();
5202
- ctx.write(`$runtime.$$awaitBlock($cd, ${n.el}, () => [${n.keywords.join(', ')}], () => ${n.exp}, $$apply,\n`);
5203
- ctx.goIndent(() => {
5204
- n.parts.forEach((part, index) => {
5205
- if(part) {
5206
- let {source, tpl, svg} = part;
5207
- ctx.writeIndent();
5208
- if(source) {
5209
- ctx.build(source);
5210
- ctx.write(',\n');
5211
- ctx.writeIndent();
5212
- } else ctx.write(`$runtime.noop, `);
5213
- ctx.build(xNode('template', {body: tpl, svg, inline: true}));
5214
- ctx.write(index == 2 ? '\n' : ',\n');
5215
- } else {
5216
- ctx.writeLine(`null, null` + (index == 2 ? '' : ','));
5217
- } });
5438
+ ctx.write(true, `$runtime.$$awaitBlock($cd, ${n.el}, () => [${n.keywords.join(', ')}], () => ${n.exp},`);
5439
+ ctx.indent++;
5440
+ n.parts.forEach((part, index) => {
5441
+ if(index) ctx.write(', ');
5442
+ if(part) {
5443
+ ctx.write(true);
5444
+ ctx.add(part.block);
5445
+ } else ctx.write('null');
5218
5446
  });
5219
- ctx.writeLine(');');
5447
+ ctx.indent--;
5448
+ ctx.write(');', true);
5220
5449
  });
5221
5450
  }
5222
5451
 
5223
- function attachSlot(slotName, label, node) {
5224
- let props = [];
5452
+ function attachSlot(slotName, node, requireCD) {
5453
+ let props = [], staticProps = true;
5454
+
5225
5455
  if(node.attributes && node.attributes.length) {
5226
5456
  node.attributes.forEach(prop => {
5227
- let name = prop.name;
5228
- let value = prop.value;
5229
- if(name[0] == '{') {
5230
- assert(value == null);
5231
- value = name;
5232
- name = unwrapExp(name);
5233
- } assert(value != null);
5234
- assert(isSimpleName(name));
5235
- if(value[0] == '{') {
5236
- value = unwrapExp(value);
5237
- this.detectDependency(value);
5457
+ let {name, value, ...ip} = this.inspectProp(prop);
5458
+ if(!ip.static) staticProps = false;
5459
+ props.push(xNode('slot-prop', {
5460
+ name,
5461
+ value
5462
+ }, (ctx, n) => {
5463
+ ctx.write(`${n.name}: ${n.value}`);
5464
+ }));
5238
5465
 
5239
- props.push(xNode('prop', {
5240
- name,
5241
- value,
5242
- dyn: true
5243
- }, (ctx, n) => {
5244
- if(this.inuse.apply) ctx.write(`${n.name}: () => (${n.value})`);
5245
- else ctx.write(`${n.name}: (${n.value})`);
5246
- }));
5247
- } else {
5248
- props.push(xNode('static-prop', {
5249
- name,
5250
- value
5251
- }, (ctx, n) => {
5252
- ctx.write(`${n.name}: \`${this.Q(n.value)}\``);
5253
- }));
5254
- }
5255
5466
  });
5256
5467
  }
5257
5468
  let placeholder;
5258
- if(node.body && node.body.length) {
5259
- let block = this.buildBlock(node, {inline: true});
5260
-
5261
- const tpl = xNode('template', {
5262
- name: '$parentElement',
5263
- body: block.tpl,
5264
- svg: block.svg
5265
- });
5469
+ if(node.body?.length) placeholder = this.buildBlock(node).block;
5266
5470
 
5267
- placeholder = xNode('placeholder', {
5268
- el: label.bindName(),
5269
- body: block.source,
5270
- tpl
5271
- }, (ctx, n) => {
5272
- ctx.build(n.tpl);
5273
- ctx.build(n.body);
5274
- ctx.writeLine(`$runtime.insertAfter(${n.el}, $parentElement);`);
5275
- });
5276
- }
5277
-
5278
- this.require('$component', '$cd', '$context');
5471
+ this.require('$context');
5472
+ this.glob.component.$value(true);
5279
5473
 
5280
- return xNode('slot', {
5474
+ let result = xNode('slot', {
5475
+ $deps: [this.glob.apply],
5281
5476
  name: slotName,
5282
- el: label.bindName(),
5283
5477
  props,
5284
- placeholder
5478
+ staticProps,
5479
+ placeholder,
5480
+ requireCD
5285
5481
  }, (ctx, n) => {
5286
- let hasDynProps = n.props.some(p => p.dyn);
5287
- let base = 'Base';
5288
- if(hasDynProps && ctx.inuse.apply) {
5289
- assert(!ctx._ctx.script.readOnly);
5290
- base = '';
5291
- }
5292
- ctx.write(true, `$runtime.attachSlot${base}($context, $cd, '${n.name}', ${n.el}, `);
5482
+ let dynamicProps = this.glob.apply.value && !n.staticProps;
5483
+
5484
+ if(dynamicProps) n.requireCD.$value(true);
5485
+
5486
+ let missed = '', slotName = n.name == 'default' ? 'null' : n.name;
5487
+ if(dynamicProps) ctx.write(`$runtime.invokeSlot($component, ${slotName}, $context`);
5488
+ else ctx.write(`$runtime.invokeSlotBase($component, ${slotName}, $context`);
5489
+
5293
5490
  if(n.props.length) {
5294
- ctx.write(`{\n`);
5295
- ctx.goIndent(() => {
5296
- for(let i=0; i < n.props.length; i++) {
5297
- let prop = n.props[i];
5298
- ctx.writeIndent();
5299
- ctx.build(prop);
5300
- if(i + 1 < n.props.length) ctx.write(',');
5301
- ctx.write('\n');
5302
- }
5491
+ if(dynamicProps) ctx.write(', () => ({');
5492
+ else ctx.write(', {');
5493
+ n.props.forEach((prop, i) => {
5494
+ if(i) ctx.write(', ');
5495
+ ctx.add(prop);
5303
5496
  });
5304
- ctx.write(true, `}`);
5305
- } else {
5306
- ctx.write(`null`);
5307
- }
5497
+ ctx.write('}');
5498
+ if(dynamicProps) ctx.write(')');
5499
+ } else missed += ', null';
5500
+
5308
5501
  if(n.placeholder) {
5309
- ctx.write(', () => {\n');
5310
- ctx.goIndent(() => {
5311
- ctx.build(n.placeholder);
5312
- });
5313
- ctx.write(true, '}');
5314
- } else if(hasDynProps && !this.config.immutable) ctx.write(`, 0`);
5315
- if(hasDynProps && !this.config.immutable) ctx.write(`, $runtime.$$compareDeep`);
5316
- ctx.write(');\n');
5502
+ ctx.write(missed, ', ');
5503
+ missed = '';
5504
+ ctx.add(n.placeholder);
5505
+ } else missed += ', null';
5506
+
5507
+ if(dynamicProps) {
5508
+ ctx.write(missed, ', ');
5509
+ if(this.config.immutable) ctx.write(`$runtime.keyComparator`);
5510
+ else ctx.write(`$runtime.$$compareDeep`);
5511
+ }
5512
+ ctx.write(')');
5317
5513
  });
5514
+ requireCD.$depends(result);
5515
+ return result;
5318
5516
  }
5319
5517
 
5320
- function makeFragment(node) {
5321
- let rx = node.value.match(/#fragment\:(\S+)(.*)$/s);
5518
+ function makeFragment(node, requireCD) {
5519
+ let rx = node.value.match(/#fragment\:(\S+)(.*)$/);
5322
5520
  assert(rx);
5323
5521
  let name = rx[1], external = false;
5324
5522
  assert(isSimpleName(name));
@@ -5333,9 +5531,12 @@
5333
5531
  });
5334
5532
  }
5335
5533
 
5534
+ if(external) requireCD.$value(true);
5535
+
5336
5536
  let block;
5337
- if(node.body && node.body.length) block = this.buildBlock({body: trimEmptyNodes(node.body)}, {inline: true, context: 'fragment'});
5338
- else {
5537
+ if(node.body && node.body.length) {
5538
+ block = this.buildBlock({body: trimEmptyNodes(node.body)}, {inline: true, context: 'fragment', parentElement: '$dom'});
5539
+ } else {
5339
5540
  this.warning(`Empty fragment: '${node.value}'`);
5340
5541
  return xNode('empty-fragment', {name}, (ctx, n) => {
5341
5542
  ctx.writeLine(`function $fragment_${n.name}() {};`);
@@ -5343,36 +5544,45 @@
5343
5544
  }
5344
5545
 
5345
5546
  return xNode('fragment', {
5547
+ $compile: [block.source, this.glob.apply],
5548
+ $deps: [block.requireCD],
5346
5549
  name,
5347
5550
  props,
5348
5551
  external,
5349
- source: block.source,
5350
- template: xNode('template', {
5351
- name: '$parentElement',
5352
- body: block.tpl,
5353
- svg: block.svg
5354
- })
5552
+ block
5355
5553
  }, (ctx, n) => {
5356
- ctx.write(true, `function $fragment_${n.name}($cd, label, $props, $events, $$fragmentSlot) {\n`);
5357
- ctx.indent++;
5358
-
5359
- if(n.props?.length) {
5360
- if(ctx.inuse.apply) {
5361
- ctx.writeLine('let ' + n.props.join(', ') + ';');
5362
- ctx.writeLine(`$runtime.unwrapProps($cd, $props, ($$) => ({${n.props.join(', ')}} = $$));`);
5363
- } else {
5364
- ctx.writeLine('let ' + n.props.join(', ') + ';');
5365
- ctx.writeLine(`$props && ({${n.props.join(', ')}} = ($runtime.isFunction($props) ? $props() : $props));`);
5554
+ if(ctx.isEmpty(n.block.source)) {
5555
+ ctx.write(true, `let $fragment_${n.name} = $runtime.makeStaticBlock(`);
5556
+ ctx.add(n.block.template);
5557
+ ctx.write(');');
5558
+ } else {
5559
+ ctx.write(true, `function $fragment_${n.name}($props, $events={}, $$fragmentSlot) {`);
5560
+ ctx.indent++;
5561
+
5562
+ if(n.block.requireCD.value) ctx.write(true, 'let $cd = $runtime.cd_new();');
5563
+
5564
+ if(n.props?.length) {
5565
+ if(this.glob.apply.value) {
5566
+ ctx.writeLine('let ' + n.props.join(', ') + ';');
5567
+ ctx.writeLine(`$runtime.unwrapProps($cd, $props, ($$) => ({${n.props.join(', ')}} = $$));`);
5568
+ } else {
5569
+ ctx.writeLine('let ' + n.props.join(', ') + ';');
5570
+ ctx.writeLine(`$props && ({${n.props.join(', ')}} = ($runtime.isFunction($props) ? $props() : $props));`);
5571
+ }
5366
5572
  }
5573
+
5574
+ ctx.write(true, 'let $dom = ');
5575
+ ctx.add(n.block.template);
5576
+ ctx.write(';');
5577
+
5578
+ ctx.add(n.block.source);
5579
+ if(n.block.requireCD.value) ctx.write(true, `return {$cd, $dom};`);
5580
+ else ctx.write(true, `return {$dom};`);
5581
+
5582
+ ctx.indent--;
5583
+ ctx.writeLine('}');
5367
5584
  }
5368
-
5369
- ctx.build(n.template);
5370
- ctx.build(n.source);
5371
- ctx.writeLine(`$runtime.insertAfter(label, $parentElement);`);
5372
-
5373
- ctx.indent--;
5374
- ctx.writeLine('}');
5375
- if(n.external) ctx.writeLine(`$runtime.exportFragment($component, '${n.name}', $fragment_${n.name});`);
5585
+ if(n.external) ctx.writeLine(`$runtime.exportFragment($cd, '${n.name}', $fragment_${n.name});`);
5376
5586
  });
5377
5587
  }
5378
5588
 
@@ -5394,7 +5604,7 @@
5394
5604
  name = name.substring(2);
5395
5605
  events.push({
5396
5606
  name,
5397
- callback: `$events?.${name}`
5607
+ callback: `$events.${name}`
5398
5608
  });
5399
5609
  }
5400
5610
  return;
@@ -5413,64 +5623,48 @@
5413
5623
  }
5414
5624
 
5415
5625
 
5416
- function attachFragment(node, element) {
5626
+ function attachFragment(node) {
5417
5627
  let name = node.elArg;
5418
5628
  assert(isSimpleName(name));
5419
5629
 
5420
- let slotBlock = null;
5421
-
5422
- if(node.body?.length) slotBlock = this.buildBlock({body: trimEmptyNodes(node.body)}, {inline: true});
5423
-
5424
- let {props, events, forwardAllEvents, staticProps} = parseAttibutes.call(this, node.attributes);
5425
- this.require('$cd');
5426
-
5427
5630
  let slot = null;
5428
- if(slotBlock) {
5429
- let template = xNode('template', {
5430
- name: '$parentElement',
5431
- body: slotBlock.tpl,
5432
- svg: slotBlock.svg,
5433
- inline: !slotBlock.source
5434
- });
5631
+ if(node.body?.length) slot = this.buildBlock({body: trimEmptyNodes(node.body)}, {inline: true});
5435
5632
 
5436
- slot = {
5437
- source: slotBlock.source,
5438
- template
5439
- };
5440
- }
5633
+ let {props, events, forwardAllEvents, staticProps} = parseAttibutes.call(this, node.attributes);
5441
5634
 
5442
5635
  return xNode('call-fragment', {
5636
+ $compile: [slot?.source],
5637
+ $deps: [this.glob.apply],
5443
5638
  forwardAllEvents,
5444
- el: element.bindName(),
5445
5639
  name,
5446
5640
  events,
5447
5641
  props,
5448
5642
  slot,
5449
5643
  staticProps
5450
5644
  }, (ctx, n) => {
5451
- ctx.write(true, `$fragment_${n.name}($cd, ${n.el}`);
5645
+ ctx.write(`$fragment_${n.name}(`);
5452
5646
  let missed = '';
5453
5647
  ctx.indent++;
5454
5648
 
5455
5649
  if(n.props.length) {
5456
- ctx.write(',\n', true);
5650
+ ctx.write(true);
5457
5651
 
5458
5652
  const writeProps = () => ctx.write('{' + n.props.map(p => p.name == p.value ? p.name : `${p.name}: ${p.value}`).join(', ') + '}');
5459
5653
 
5460
- if(n.staticProps) writeProps();
5654
+ if(n.staticProps || !this.glob.apply.value) writeProps();
5461
5655
  else {
5462
5656
  ctx.write(`() => (`);
5463
5657
  writeProps();
5464
5658
  ctx.write(`)`);
5465
5659
  }
5466
- } else missed = ', 0';
5660
+ } else missed = 'null';
5467
5661
 
5468
5662
  if(n.forwardAllEvents) {
5469
5663
  if(n.events.length) this.warning(`Fragment: mixing binding and forwarding is not supported: '${node.openTag}'`);
5470
5664
  ctx.write(missed, ', $events');
5471
5665
  missed = '';
5472
5666
  } else if(n.events.length) {
5473
- ctx.write(missed, ',\n', true, '{');
5667
+ ctx.write(missed, ',', true, '{');
5474
5668
  missed = '';
5475
5669
 
5476
5670
  n.events.forEach((e, i) => {
@@ -5488,42 +5682,42 @@
5488
5682
  } else missed += ', 0';
5489
5683
 
5490
5684
  if(n.slot) {
5491
- ctx.write(missed, ',\n');
5685
+ ctx.write(missed, ',', true);
5492
5686
  missed = '';
5493
- if(n.slot.source) {
5494
- ctx.writeLine(`($cd, label) => {`);
5495
- ctx.goIndent(() => {
5496
- ctx.build(n.slot.template);
5497
- ctx.build(n.slot.source);
5498
- ctx.writeLine(`$runtime.insertAfter(label, $parentElement);`);
5499
- });
5500
- ctx.write(true, `}`);
5687
+ if(ctx.isEmpty(n.slot.source)) {
5688
+ ctx.write(`$runtime.makeStaticBlock(`);
5689
+ ctx.add(n.slot.template);
5690
+ ctx.write(`)`);
5501
5691
  } else {
5502
- ctx.write(true, `($cd, label) => $runtime.insertAfter(label, `);
5503
- ctx.build(n.slot.template);
5504
- ctx.write(`)\n`);
5692
+ ctx.write(`$runtime.makeBlock(`);
5693
+ ctx.add(n.slot.template);
5694
+ ctx.write(`, ($cd, $parentElement) => {`, true);
5695
+ ctx.indent++;
5696
+ ctx.add(n.slot.source);
5697
+ ctx.indent--;
5698
+ ctx.write(true, `})`);
5505
5699
  }
5506
5700
  }
5507
5701
 
5508
5702
  ctx.indent--;
5509
- if(n.props.length || n.events.length || n.slot) ctx.write(true, ');\n');
5510
- else ctx.write(');\n');
5703
+ if(n.props.length || n.events.length || n.slot) ctx.write(true, ')');
5704
+ else ctx.write(')');
5511
5705
 
5512
5706
  });
5513
5707
  }
5514
5708
 
5515
- function attachFragmentSlot(label) {
5516
- this.require('$cd');
5709
+ function attachFragmentSlot(label, requireCD) {
5710
+ requireCD.$value(true);
5517
5711
 
5518
5712
  return xNode('fragment-slot', {
5519
5713
  el: label.bindName()
5520
5714
  }, (ctx, n) => {
5521
- ctx.writeLine(`$$fragmentSlot?.($cd, ${n.el});`);
5715
+ ctx.write(true, `$runtime.attachBlock($cd, ${n.el}, $$fragmentSlot?.())`);
5522
5716
  });
5523
5717
  }
5524
5718
 
5525
- function attchExportedFragment(node, label, componentName) {
5526
- this.require('$cd');
5719
+ function attchExportedFragment(node, label, componentName, requireCD) {
5720
+ requireCD.$value(true);
5527
5721
 
5528
5722
  let data = {
5529
5723
  name: node.elArg,
@@ -5533,48 +5727,44 @@
5533
5727
 
5534
5728
  let body = trimEmptyNodes(node.body || []);
5535
5729
  if(body.length) {
5536
- let block = this.buildBlock({body}, {inline: true});
5537
- assert(!block.svg, 'SVG is not supported for exported fragment');
5538
- data.source = block.source;
5539
- data.template = xNode('template', {
5540
- raw: true,
5541
- body: block.tpl
5542
- });
5730
+ data.slot = this.buildBlock({body}, {inline: true});
5731
+ data.$compile = [data.slot.source];
5732
+ data.$deps = [data.slot.requireCD];
5733
+ // assert(!data.slot.template.svg, 'SVG is not supported for exported fragment');
5543
5734
  }
5544
5735
 
5545
5736
  let pa = parseAttibutes.call(this, node.attributes);
5546
- data.props = pa.props;
5547
- data.events = pa.events;
5548
- data.forwardAllEvents = pa.forwardAllEvents;
5549
- data.staticProps = pa.staticProps;
5737
+ data = {...pa, ...data};
5550
5738
 
5551
5739
  return xNode('attach-exported-fragment', data, (ctx, n) => {
5552
- ctx.write(true, `$runtime.attchExportedFragment($cd, $instance_${n.componentName}, '${n.name}', ${n.label}`);
5553
- let missed = '';
5740
+ ctx.write(true, `$runtime.attachBlock($cd, ${n.label}, $runtime.callExportedFragment($instance_${n.componentName}, '${n.name}'`);
5554
5741
  ctx.indent++;
5742
+ let missed = '';
5555
5743
 
5556
- if(n.props.length) {
5557
- ctx.write(',\n', true);
5558
-
5559
- const writeProps = () => ctx.write('{' + n.props.map(p => p.name == p.value ? p.name : `${p.name}: ${p.value}`).join(', ') + '}');
5744
+ if(n.slot) {
5745
+ ctx.write(',', true);
5560
5746
 
5561
- if(n.staticProps) writeProps();
5562
- else {
5563
- ctx.write(`$runtime.observeProps(`);
5564
- if(this.config.immutable) ctx.write(`$runtime.keyComparator`);
5565
- else ctx.write(`$runtime.$$compareDeep`);
5566
- ctx.write(', () => (');
5567
- writeProps();
5568
- ctx.write('))');
5747
+ if(ctx.isEmpty(n.slot.source)) {
5748
+ ctx.write(`$runtime.makeStaticBlock(`);
5749
+ ctx.add(n.slot.template);
5750
+ ctx.write(`)`);
5751
+ } else {
5752
+ ctx.write(`$runtime.makeBlockBound($cd, `);
5753
+ ctx.add(n.slot.template);
5754
+ ctx.write(`, ($cd, $parentElement) => {`, true);
5755
+ ctx.indent++;
5756
+ ctx.add(n.slot.source);
5757
+ ctx.indent--;
5758
+ ctx.write(true, `})`);
5569
5759
  }
5570
- } else missed = ', 0';
5760
+ } else missed = ', null';
5571
5761
 
5572
5762
  if(n.forwardAllEvents) {
5573
5763
  if(n.events.length) this.warning(`Fragment: mixing binding and forwarding is not supported: '${node.openTag}'`);
5574
5764
  ctx.write(missed, ', $events');
5575
5765
  missed = '';
5576
5766
  } else if(n.events.length) {
5577
- ctx.write(missed, ',\n', true, '{');
5767
+ ctx.write(missed, ',', true, '{');
5578
5768
  missed = '';
5579
5769
 
5580
5770
  n.events.forEach((e, i) => {
@@ -5589,27 +5779,32 @@
5589
5779
  }
5590
5780
  });
5591
5781
  ctx.write('}');
5592
- } else missed += ', 0';
5782
+ } else missed += ', null';
5593
5783
 
5594
- if(n.template) {
5595
- if(missed) ctx.write(missed, `, \``);
5596
- else ctx.write(`,\n`, true, `\``);
5597
- ctx.build(n.template);
5598
- if(n.source) {
5599
- ctx.write(`\`, ($cd, $parentElement) => {\n`);
5600
- ctx.indent++;
5601
- ctx.build(n.source);
5602
- ctx.indent--;
5603
- ctx.writeLine(`});`);
5604
- } else {
5605
- ctx.write(`\`);\n`);
5784
+ if(n.props.length) {
5785
+ if(missed) ctx.write(missed);
5786
+ missed = '';
5787
+ ctx.write(',', true);
5788
+
5789
+ const writeProps = () => ctx.write('{' + n.props.map(p => p.name == p.value ? p.name : `${p.name}: ${p.value}`).join(', ') + '}');
5790
+
5791
+ if(n.staticProps) writeProps();
5792
+ else {
5793
+ ctx.write(`() => (`);
5794
+ writeProps();
5795
+ ctx.write(`), `);
5796
+ if(this.config.immutable) ctx.write(`$runtime.keyComparator`);
5797
+ else ctx.write(`$runtime.$$compareDeep`);
5606
5798
  }
5607
- } else ctx.write(');\n');
5799
+
5800
+ }
5801
+
5608
5802
  ctx.indent--;
5803
+ ctx.write('));');
5609
5804
  });
5610
5805
  }
5611
5806
 
5612
- function attachHead(n) {
5807
+ function attachHead(n, requireCD) {
5613
5808
  if(n.elArg == 'window' || n.elArg == 'body') {
5614
5809
  let name = 'el' + (this.uniqIndex++);
5615
5810
  let block = this.buildBlock({body: [n]}, {malinaElement: true, inline: true, oneElement: name, bindAttributes: true});
@@ -5635,7 +5830,10 @@
5635
5830
  return true;
5636
5831
  });
5637
5832
 
5638
- let d = {};
5833
+ let d = {
5834
+ $deps: [this.glob.apply],
5835
+ requireCD
5836
+ };
5639
5837
  if(title?.body?.[0]) {
5640
5838
  assert(title.body[0].type == 'text');
5641
5839
  let r = this.parseText(title.body[0].value);
@@ -5646,27 +5844,39 @@
5646
5844
  }
5647
5845
  }
5648
5846
  if(body.length) {
5649
- let block = this.buildBlock({body}, {inline: true});
5650
- d.source = block.source;
5651
- d.template = xNode('template', {
5652
- name: '$parentElement',
5653
- body: block.tpl
5847
+ let bb = this.buildBlock({body}, {
5848
+ inline: true,
5849
+ template: {
5850
+ name: '$parentElement',
5851
+ cloneNode: true,
5852
+ requireFragment: true
5853
+ }
5654
5854
  });
5855
+ d.source = bb.source;
5856
+ d.template = bb.template;
5857
+ d.blockCD = bb.requireCD;
5858
+
5859
+ d.$compile = [d.source];
5860
+ d.$deps.push(d.blockCD);
5861
+
5655
5862
  this.require('$onDestroy');
5656
5863
  }
5657
5864
 
5658
- return xNode('malina:head', d, (ctx, n) => {
5865
+ const result = xNode('malina:head', d, (ctx, n) => {
5866
+ if(n.blockCD.value) n.requireCD.$value(true);
5659
5867
  if(n.title != null) ctx.writeLine(`document.title = ${n.title};`);
5660
5868
  if(n.dynTitle) {
5661
- if(ctx.inuse.apply) ctx.writeLine(`$watchReadOnly($cd, () => (${n.dynTitle}), (value) => {document.title = value;});`);
5662
- else ctx.writeLine(`document.title = ${n.dynTitle};`);
5869
+ if(this.glob.apply.value) {
5870
+ n.requireCD.$value(true);
5871
+ ctx.writeLine(`$watchReadOnly($cd, () => (${n.dynTitle}), (value) => {document.title = value;});`);
5872
+ } else ctx.writeLine(`document.title = ${n.dynTitle};`);
5663
5873
  }
5664
5874
 
5665
5875
  if(n.template) {
5666
5876
  ctx.writeLine(`{`);
5667
5877
  ctx.indent++;
5668
- ctx.build(n.template);
5669
- ctx.build(n.source);
5878
+ ctx.add(n.template);
5879
+ ctx.add(n.source);
5670
5880
  ctx.writeLine(`let a=$parentElement.firstChild, b=$parentElement.lastChild;`);
5671
5881
  ctx.writeLine(`$onDestroy(() => {$runtime.$$removeElements(a, b)});`);
5672
5882
  ctx.writeLine(`document.head.appendChild($parentElement);`);
@@ -5674,6 +5884,8 @@
5674
5884
  ctx.writeLine(`}`);
5675
5885
  }
5676
5886
  });
5887
+ requireCD.$depends(result);
5888
+ return result;
5677
5889
  } else throw 'Wrong tag';
5678
5890
  }
5679
5891
 
@@ -5727,37 +5939,47 @@
5727
5939
  return {name, value, rawValue, static: statical};
5728
5940
  }
5729
5941
 
5730
- function attachPortal(node) {
5942
+ function attachPortal(node, requireCD) {
5731
5943
  let body = trimEmptyNodes(node.body || []);
5732
5944
  if(!body.length) return;
5733
- let block = this.buildBlock({body}, {inline: true});
5945
+
5946
+ let bb = this.buildBlock({body}, {
5947
+ inline: true,
5948
+ template: {
5949
+ name: '$parentElement',
5950
+ cloneNode: true,
5951
+ requireFragment: true
5952
+ }
5953
+ });
5954
+
5955
+ this.require('$component');
5734
5956
 
5735
5957
  let mount = node.attributes.find(a => a.name == 'mount')?.value;
5736
5958
  if(mount) mount = unwrapExp(mount);
5737
5959
 
5738
- this.require('$cd');
5739
-
5740
- return xNode('portal', {
5960
+ const result = xNode('portal', {
5961
+ $compile: [bb.source],
5962
+ $deps: [bb.requireCD],
5741
5963
  mount,
5742
- source: block.source,
5743
- template: xNode('template', {
5744
- name: '$parentElement',
5745
- body: block.tpl,
5746
- svg: block.svg
5747
- })
5964
+ source: bb.source,
5965
+ template: bb.template,
5966
+ requireCD
5748
5967
  }, (ctx, n) => {
5968
+ if(n.$deps[0].value) n.requireCD.$value(true);
5749
5969
  let label = n.mount || 'document.body';
5750
5970
  ctx.writeLine('{');
5751
5971
  ctx.indent++;
5752
- ctx.build(n.template);
5753
- ctx.build(n.source);
5972
+ ctx.add(n.template);
5973
+ ctx.add(n.source);
5754
5974
  ctx.writeLine(`let $$first = $parentElement[$runtime.firstChild];`);
5755
5975
  ctx.writeLine(`let $$last = $parentElement.lastChild;`);
5756
- ctx.writeLine(`$runtime.cd_onDestroy($cd, () => $runtime.$$removeElements($$first, $$last));`);
5976
+ ctx.writeLine(`$runtime.cd_onDestroy(${n.$deps[0].value ? '$cd' : '$component'}, () => $runtime.$$removeElements($$first, $$last));`);
5757
5977
  ctx.writeLine(`$tick(() => ${label}.appendChild($parentElement));`);
5758
5978
  ctx.indent--;
5759
5979
  ctx.writeLine('}');
5760
5980
  });
5981
+ requireCD.$depends(result);
5982
+ return result;
5761
5983
  }
5762
5984
 
5763
5985
  function makeEventProp(prop, requireElement) {
@@ -5780,6 +6002,7 @@
5780
6002
  let modList = [], _mod = '';
5781
6003
  let handler = '', exp, func;
5782
6004
  let step = 0;
6005
+ let rootModifier = false;
5783
6006
  for(let a of name) {
5784
6007
  if(a == '|') {
5785
6008
  assert$1(step <= 1);
@@ -5807,14 +6030,18 @@
5807
6030
 
5808
6031
  this.detectDependency(exp || handler);
5809
6032
 
6033
+ let globalFunction = false;
5810
6034
  if(exp) {
5811
6035
  let type = detectExpressionType(exp);
5812
6036
  if(type == 'identifier') {
6037
+ globalFunction = !!this.script.rootFunctions[exp];
5813
6038
  handler = exp;
5814
6039
  exp = null;
5815
6040
  } else if(type == 'function') {
5816
6041
  func = exp;
5817
6042
  exp = null;
6043
+ } else if(type?.type == 'function-call') {
6044
+ globalFunction = !!this.script.rootFunctions[type.name];
5818
6045
  } }
5819
6046
 
5820
6047
  // modifiers
@@ -5835,6 +6062,10 @@
5835
6062
  let mods = [];
5836
6063
  let preventInserted;
5837
6064
  modList.forEach(opt => {
6065
+ if(opt == 'root') {
6066
+ rootModifier = true;
6067
+ return;
6068
+ }
5838
6069
  if(opt == 'preventDefault' || opt == 'prevent') {
5839
6070
  if(preventInserted) return;
5840
6071
  mods.push('$event.preventDefault();');
@@ -5873,7 +6104,8 @@
5873
6104
  exp,
5874
6105
  handlerName: handler,
5875
6106
  func,
5876
- mods
6107
+ mods,
6108
+ globalFunction
5877
6109
  }, (ctx, n) => {
5878
6110
  if(n.handlerName && !ctx.inuse.apply && !n.mods) return ctx.write(n.handlerName);
5879
6111
  ctx.write(`($event) => { `);
@@ -5883,14 +6115,14 @@
5883
6115
  if(last(n.exp) != ';') n.exp += ';';
5884
6116
  ctx.write(`${n.exp}`);
5885
6117
  } else if(n.func) ctx.write(`(${n.func})($event);`);
5886
- if(ctx.inuse.apply) ctx.write(` $$apply();`);
6118
+ if(ctx.inuse.apply && !n.globalFunction) ctx.write(` $$apply();`);
5887
6119
  ctx.write(`}`);
5888
6120
  });
5889
6121
 
5890
- return {event, fn};
6122
+ return {event, fn, rootModifier};
5891
6123
  }
5892
6124
 
5893
- const version = '0.6.50';
6125
+ const version = '0.7.1-alpha';
5894
6126
 
5895
6127
 
5896
6128
  async function compile(source, config = {}) {
@@ -5923,6 +6155,7 @@
5923
6155
  makeEachBlock,
5924
6156
  makeifBlock,
5925
6157
  makeComponent,
6158
+ makeComponentDyn,
5926
6159
  makeHtmlBlock,
5927
6160
  parseText,
5928
6161
  makeAwaitBlock,
@@ -5938,17 +6171,27 @@
5938
6171
  checkRootName: checkRootName,
5939
6172
 
5940
6173
  inuse: {},
5941
- require: function() {
5942
- for(let name of arguments) {
6174
+ glob: {
6175
+ apply: xNode('apply', false),
6176
+ component: xNode('$component', false),
6177
+ componentFn: xNode('componentFn', false),
6178
+ rootCD: xNode('root-cd', false)
6179
+ },
6180
+ require: function(...args) {
6181
+ for(let name of args) {
5943
6182
  let deps = true;
5944
6183
  if(name == '$props:no-deps') {name = '$props'; deps = false;} if(name == 'apply' && ctx.script.readOnly) name = 'blankApply';
5945
6184
  if(ctx.inuse[name] == null) ctx.inuse[name] = 0;
5946
6185
  ctx.inuse[name]++;
5947
6186
  if(!deps) continue;
6187
+ if(name == 'apply') ctx.glob.apply.$value(true);
6188
+ if(name == '$component') ctx.glob.component.$value(true);
5948
6189
  if(name == '$attributes') ctx.require('$props');
5949
6190
  if(name == '$props' && !ctx.script.readOnly) ctx.require('apply', '$cd');
5950
- if(name == 'apply' && !ctx.script.readOnly) ctx.require('$cd');
5951
- if(name == '$cd') ctx.require('$component');
6191
+ if(name == '$cd') {
6192
+ ctx.glob.rootCD.$value(true);
6193
+ ctx.require('$component');
6194
+ }
5952
6195
  if(name == '$onDestroy') ctx.require('$component');
5953
6196
  if(name == '$onMount') ctx.require('$component');
5954
6197
  }
@@ -5981,9 +6224,7 @@
5981
6224
  },
5982
6225
 
5983
6226
  xBuild: node => {
5984
- let w = new xWriter(ctx);
5985
- w.build(node);
5986
- return w.toString();
6227
+ return xBuild(ctx, node);
5987
6228
  }
5988
6229
  };
5989
6230
 
@@ -6035,48 +6276,9 @@
6035
6276
  result.push(`import { $$htmlToFragment } from 'malinajs/runtime.js';`);
6036
6277
  }
6037
6278
  result.push(ctx.module.top);
6279
+ result.push(makeComponentFn.call(ctx));
6038
6280
 
6039
- result.push(xNode('block', {
6040
- name: config.name,
6041
- body: [ctx.module.head, ctx.module.code, ctx.module.body]
6042
- }, (ctx, n) => {
6043
- ctx.writeIndent();
6044
- if(config.exportDefault) ctx.write('export default ');
6045
- else ctx.write(`const ${n.name} = `);
6046
-
6047
- let component = xNode('function', {
6048
- args: ['$option'],
6049
- inline: true,
6050
- arrow: true,
6051
- body: n.body
6052
- });
6053
-
6054
- if(ctx.inuse.apply) {
6055
- ctx.write('$runtime.makeComponent(');
6056
- component.args.push('$$apply');
6057
- ctx.build(component);
6058
- ctx.write(', $runtime.$base);\n');
6059
- } else if(ctx.inuse.$cd || ctx.inuse.$component || ctx.inuse.$context || ctx.inuse.blankApply) {
6060
- if(ctx.inuse.blankApply) {
6061
- component.body[0].body.unshift(xNode('block', (ctx) => {
6062
- ctx.writeLine('let $$apply = $runtime.noop;');
6063
- }));
6064
- }
6065
-
6066
- ctx.write('$runtime.makeComponent(');
6067
- ctx.build(component);
6068
- ctx.write(', $runtime.$readOnlyBase);\n');
6069
- } else {
6070
- ctx.write('($element, $option={}) => {\n');
6071
- ctx.goIndent(() => {
6072
- ctx.inuse.$insertElementByOption = true;
6073
- ctx.build(xNode('block', {body: n.body}));
6074
- });
6075
- ctx.write('}');
6076
- }
6077
- }));
6078
-
6079
- ctx.result = ctx.xBuild(result);
6281
+ ctx.result = xBuild(ctx, result);
6080
6282
 
6081
6283
  await hook(ctx, 'build');
6082
6284
  return ctx;
@@ -6125,8 +6327,7 @@
6125
6327
 
6126
6328
  if(localConfig) {
6127
6329
  const confFn = require(localConfig);
6128
- if(typeof(confFn) == 'function') result = confFn(result, filename);
6129
- else result = confFn;
6330
+ result = confFn(result, filename);
6130
6331
  }
6131
6332
  if(!result.path) result.path = filename;
6132
6333
  if(!result.name) result.name = filename.match(/([^/\\]+)\.\w+$/)[1];
@@ -6134,6 +6335,60 @@
6134
6335
  return result;
6135
6336
  }
6136
6337
 
6338
+
6339
+ function makeComponentFn() {
6340
+ let componentFn = xNode('componentFn', {
6341
+ $deps: [this.glob.apply, this.glob.rootCD],
6342
+ body: [this.module.head, this.module.code, this.module.body],
6343
+ }, (ctx, n) => {
6344
+ let component = xNode('function', {
6345
+ args: ['$option'],
6346
+ inline: true,
6347
+ arrow: true,
6348
+ body: n.body
6349
+ });
6350
+
6351
+ if(this.glob.apply.value) {
6352
+ ctx.add(this.glob.componentFn);
6353
+ ctx.write('$runtime.makeComponent(');
6354
+ component.args.push('$$apply');
6355
+ ctx.add(component);
6356
+ ctx.write(', $runtime.$base);', true);
6357
+ } else if(this.glob.rootCD.value || ctx.inuse.$cd || ctx.inuse.$component || ctx.inuse.$context || ctx.inuse.blankApply) {
6358
+ ctx.add(this.glob.componentFn);
6359
+ if(ctx.inuse.blankApply) {
6360
+ component.body[0].body.unshift(xNode('block', (ctx) => {
6361
+ ctx.writeLine('let $$apply = $runtime.noop;');
6362
+ }));
6363
+ }
6364
+
6365
+ ctx.write('$runtime.makeComponent(');
6366
+ ctx.add(component);
6367
+ ctx.write(');', true);
6368
+ } else {
6369
+ this.glob.componentFn.$value('thin');
6370
+ ctx.add(this.glob.componentFn);
6371
+ ctx.write('($option={}) => {', true);
6372
+ ctx.goIndent(() => {
6373
+ ctx.add(xNode('block', {body: n.body}));
6374
+ });
6375
+ ctx.write(true, '}');
6376
+ }
6377
+ });
6378
+
6379
+ return xNode('block', {
6380
+ $compile: [this.module.head, this.module.code, this.module.body],
6381
+ name: this.config.name,
6382
+ componentFn
6383
+ }, (ctx, n) => {
6384
+ ctx.writeIndent();
6385
+ if(this.config.exportDefault) ctx.write('export default ');
6386
+ else ctx.write(`const ${n.name} = `);
6387
+ ctx.add(this.glob.apply);
6388
+ ctx.add(n.componentFn);
6389
+ });
6390
+ }
6391
+
6137
6392
  exports.compile = compile;
6138
6393
  exports.version = version;
6139
6394