malinajs 0.6.52 → 0.7.0-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.
Files changed (5) hide show
  1. package/compile.js +1304 -1050
  2. package/malina-esbuild.js +10 -4
  3. package/malina.js +1305 -1051
  4. package/package.json +1 -1
  5. package/runtime.js +433 -301
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,94 +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
- this.require('$events');
4302
4502
  return false;
4303
4503
  } else if(name == 'this') {
4304
- dynamicComponent = unwrapExp(value);
4305
4504
  return false;
4306
4505
  }
4307
4506
  return true;
@@ -4351,35 +4550,21 @@
4351
4550
  if(name.startsWith('@@')) {
4352
4551
  let event = name.substring(2);
4353
4552
  assert(!value);
4354
- passOption.events = true;
4355
- boundEvent(event);
4356
4553
  this.require('$events');
4357
- head.push(xNode('forwardEvent', {
4554
+ passEvent(event, xNode('forwardEvent', {
4358
4555
  event
4359
- }, (ctx, data) => {
4360
- if(_boundEvents[data.event] > 1) ctx.writeLine(`$runtime.$$addEventForComponent(events, '${data.event}', $events.${data.event});`);
4361
- else ctx.writeLine(`events.${data.event} = $events.${data.event};`);
4556
+ }, (ctx, n) => {
4557
+ ctx.write(`$events.${n.event}`);
4362
4558
  }));
4363
4559
  return;
4364
4560
  }
4365
4561
 
4366
4562
  let {event, fn} = this.makeEventProp(prop);
4367
4563
 
4368
- passOption.events = true;
4369
- boundEvent(event);
4370
- head.push(xNode('passEvent', {
4371
- event,
4564
+ passEvent(event, xNode('passEvent', {
4372
4565
  fn
4373
4566
  }, (ctx, n) => {
4374
- if(_boundEvents[n.event] > 1) {
4375
- ctx.write(true, `$runtime.$$addEventForComponent(events, '${n.event}', `);
4376
- ctx.build(n.fn);
4377
- ctx.write(`);\n`);
4378
- } else {
4379
- ctx.write(true, `events.${n.event} = `);
4380
- ctx.build(n.fn);
4381
- ctx.write(`;\n`);
4382
- }
4567
+ ctx.add(n.fn);
4383
4568
  }));
4384
4569
  return;
4385
4570
  } else if(this.config.passClass && (name == 'class' || name.startsWith('class:'))) {
@@ -4392,6 +4577,7 @@
4392
4577
  assert(metaClass);
4393
4578
  }
4394
4579
  assert(value);
4580
+ this.css.passingClass = true;
4395
4581
 
4396
4582
  const parsed = this.parseText(prop.value);
4397
4583
  this.detectDependency(parsed);
@@ -4409,41 +4595,87 @@
4409
4595
  });
4410
4596
 
4411
4597
 
4412
- if(forwardAllEvents || passOption.events) options.push('events');
4413
- if(passOption.slots) options.push('slots');
4414
- if(passOption.anchor) options.push('anchor');
4598
+ if(Object.keys(events).length == 0) events = null;
4415
4599
 
4416
4600
  let result = xNode('component', {
4417
- el: element.bindName(),
4418
4601
  componentName,
4419
- head,
4420
- options,
4421
- $cd: '$cd',
4422
4602
  staticProps,
4423
4603
  props: propsFn,
4424
4604
  propsSetter,
4425
4605
  reference,
4426
- $class
4606
+ $class,
4607
+ forwardAllEvents,
4608
+ events,
4609
+ slots: slotBlocks.length ? slotBlocks : null,
4610
+ anchors: anchorBlocks.length ? anchorBlocks : null
4427
4611
  }, (ctx, n) => {
4428
- const $cd = n.$cd || '$cd';
4429
-
4430
- let head = ctx.subBuild(n.head);
4431
- if(head) {
4432
- ctx.addBlock(head);
4433
- n.requireScope = true;
4434
- }
4612
+ if(n.reference) throw 'not implemented'; // FIXME
4613
+ let comma = false;
4614
+ ctx.write(`$runtime.callComponent($context, ${n.componentName}, {`);
4435
4615
 
4436
4616
  if(n.props.length && n.staticProps) {
4437
- n.options.push(`props: {${n.props.join(', ')}}`);
4617
+ ctx.write(`props: {${n.props.join(', ')}}`);
4618
+ comma = true;
4438
4619
  n.props = [];
4439
4620
  }
4440
-
4441
- ctx.write(true);
4442
- if(n.reference) ctx.write(`${n.reference} = `);
4443
- 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('}');
4444
4677
 
4445
4678
  let other = '';
4446
- ctx.indent++;
4447
4679
  if(n.props.length) ctx.write(`,\n`, true, `() => ({${n.props.join(', ')}})`);
4448
4680
  else other = ', null';
4449
4681
 
@@ -4469,77 +4701,61 @@
4469
4701
  } else other += ', null';
4470
4702
 
4471
4703
  ctx.indent--;
4472
- ctx.write(`);\n`);
4704
+ ctx.write(true, `)`);
4473
4705
  });
4474
4706
 
4475
- if(!dynamicComponent) {
4476
- return {bind: xNode('component-scope', {
4477
- component: result
4478
- }, (ctx, n) => {
4479
- let r = ctx.subBuild(n.component);
4480
-
4481
- if(n.component.requireScope) {
4482
- ctx.writeLine('{');
4483
- ctx.goIndent(() => {
4484
- ctx.addBlock(r);
4485
- });
4486
- ctx.writeLine('}');
4487
- } else ctx.addBlock(r);
4488
- })};
4489
- } else {
4490
- this.detectDependency(dynamicComponent);
4707
+ return {bind: result};
4708
+ }
4709
+ function makeComponentDyn(node, requireCD, element) {
4710
+ let dynamicComponent;
4491
4711
 
4492
- result.componentName = '$ComponentConstructor';
4493
- result.$cd = 'childCD';
4494
- return {bind: xNode('dyn-component', {
4495
- el: element.bindName(),
4496
- exp: dynamicComponent,
4497
- component: result
4498
- }, (ctx, n) => {
4499
- ctx.writeLine('{');
4500
- ctx.goIndent(() => {
4501
- if(ctx.inuse.apply) {
4502
- ctx.writeLine(`let childCD, finalLabel = $runtime.getFinalLabel(${n.el});`);
4503
- ctx.writeLine(`$watch($cd, () => (${n.exp}), ($ComponentConstructor) => {`);
4504
- ctx.goIndent(() => {
4505
- ctx.writeLine(`if(childCD) {`);
4506
- ctx.goIndent(() => {
4507
- ctx.writeLine(`childCD.destroy();`);
4508
- ctx.writeLine(`$runtime.removeElementsBetween(${n.el}, finalLabel);`);
4509
- });
4510
- ctx.writeLine(`}`);
4511
- ctx.writeLine(`childCD = null;`);
4512
- ctx.writeLine(`if($ComponentConstructor) {`);
4513
- ctx.goIndent(() => {
4514
- ctx.writeLine(`childCD = $cd.new();`);
4515
- ctx.build(n.component);
4516
- });
4517
- ctx.writeLine(`}`);
4518
- });
4519
- ctx.writeLine(`});`);
4520
- } else {
4521
- ctx.writeLine(`let $ComponentConstructor = ${n.exp};`);
4522
- ctx.writeLine(`if($ComponentConstructor) {`);
4523
- ctx.goIndent(() => {
4524
- ctx.writeLine(`let childCD = $cd;`);
4525
- ctx.build(n.component);
4526
- });
4527
- ctx.writeLine(`}`);
4528
- }
4529
- });
4530
- ctx.writeLine('}');
4531
- })};
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
+ });
4532
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
+ });
4533
4739
  }
4534
4740
 
4535
- function bindProp(prop, node, element) {
4741
+ function bindProp(prop, node, element, requireCD) {
4536
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
+
4537
4754
  if(prop.name[0] == '@' || prop.name.startsWith('on:')) name = 'event';
4538
- if(!name && prop.name[0] == ':') {
4755
+ else if(prop.name[0] == ':') {
4539
4756
  name = 'bind';
4540
4757
  arg = prop.name.substring(1);
4541
- }
4542
- if(!name && prop.name[0] == '*') {
4758
+ } else if(prop.name[0] == '*') {
4543
4759
  let rx = prop.name.match(/^\*\{.*\}$/);
4544
4760
  if(rx) {
4545
4761
  assert(prop.value == null, 'wrong binding: ' + prop.content);
@@ -4549,17 +4765,7 @@
4549
4765
  name = 'use';
4550
4766
  arg = prop.name.substring(1);
4551
4767
  }
4552
- }
4553
- if(prop.content.startsWith('{*')) {
4554
- const pe = this.parseText(prop.content);
4555
- assert(pe.parts[0].type == 'js');
4556
- let exp = pe.parts[0].value;
4557
- if(!exp.endsWith(';')) exp += ';';
4558
- return {bind: xNode('block', {body: [
4559
- replaceElementKeyword(exp, () => element.bindName())
4560
- ]})};
4561
- }
4562
- if(!name && prop.value == null) {
4768
+ } else if(prop.value == null) {
4563
4769
  let rx = prop.name.match(/^\{(.*)\}$/);
4564
4770
  if(rx) {
4565
4771
  name = rx[1];
@@ -4567,7 +4773,6 @@
4567
4773
  // spread operator
4568
4774
  name = name.substring(3);
4569
4775
  assert(detectExpressionType(name) == 'identifier');
4570
- this.detectDependency(name);
4571
4776
  return node.spreading.push(`...${name}`);
4572
4777
  } else {
4573
4778
  prop.value = prop.name;
@@ -4598,7 +4803,8 @@
4598
4803
  } else if(name == 'event') {
4599
4804
  if(prop.name.startsWith('@@')) {
4600
4805
  assert(!prop.value);
4601
- this.require('$cd', '$events');
4806
+ requireCD.$value(true);
4807
+ this.require('$events');
4602
4808
  if(prop.name == '@@') {
4603
4809
  return {bind: xNode('forwardAllEvents', {
4604
4810
  el: element.bindName()
@@ -4618,18 +4824,20 @@
4618
4824
  })};
4619
4825
  }
4620
4826
 
4621
- let {event, fn} = this.makeEventProp(prop, () => element.bindName());
4622
-
4623
- this.require('$cd');
4827
+ let {event, fn, rootModifier} = this.makeEventProp(prop, () => element.bindName());
4828
+ if(rootModifier) this.require('rootEvent');
4829
+ else requireCD.$value(true);
4624
4830
 
4625
4831
  return {bind: xNode('bindEvent', {
4626
4832
  event,
4627
4833
  fn,
4628
- el: element.bindName()
4834
+ el: element.bindName(),
4835
+ rootModifier
4629
4836
  }, (ctx, n) => {
4630
- 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}', `);
4631
4839
  ctx.build(n.fn);
4632
- ctx.write(`);\n`);
4840
+ ctx.write(`);`);
4633
4841
  })};
4634
4842
  } else if(name == 'bind') {
4635
4843
  if(this.script.readOnly) {
@@ -4637,7 +4845,8 @@
4637
4845
  return;
4638
4846
  }
4639
4847
 
4640
- this.require('apply', '$cd');
4848
+ requireCD.$value(true);
4849
+ this.require('apply');
4641
4850
  let exp;
4642
4851
  arg = arg.split(/[\:\|]/);
4643
4852
  let attr = arg.shift();
@@ -4716,7 +4925,7 @@
4716
4925
  })};
4717
4926
  } else if(name == 'use') {
4718
4927
  if(arg) {
4719
- this.require('$cd');
4928
+ requireCD.$value(true);
4720
4929
  assert(isSimpleName(arg), 'Wrong name: ' + arg);
4721
4930
  this.checkRootName(arg);
4722
4931
  let args = prop.value ? `, () => [${getExpression()}]` : '';
@@ -4780,8 +4989,7 @@
4780
4989
  });
4781
4990
 
4782
4991
  if(compound) {
4783
- let defaultHash = '';
4784
- if(node.classes.has(this.css.id)) defaultHash = this.css.id;
4992
+ let classes = Array.from(node.classes);
4785
4993
  node.classes.clear();
4786
4994
  if(this.config.passClass) this.require('resolveClass');
4787
4995
  let exp = props.map(prop => {
@@ -4798,24 +5006,40 @@
4798
5006
  const bind = xNode('compound-class', {
4799
5007
  el: element.bindName(),
4800
5008
  exp,
4801
- defaultHash
5009
+ classes,
5010
+ requireCD
4802
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
+
4803
5026
  if(ctx.inuse.resolveClass) {
4804
- let base = n.defaultHash ? `,'${n.defaultHash}'` : '';
4805
5027
  if(ctx.inuse.apply) {
4806
- 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}))`);
4807
5030
  } else {
4808
- ctx.writeLine(`$runtime.setClassToElement(${n.el}, $$resolveClass((${n.exp})${base}));`);
5031
+ ctx.write(true, `$runtime.setClassToElement(${n.el}, $$resolveClass((${n.exp})${base}));`);
4809
5032
  }
4810
5033
  } else {
4811
- let base = n.defaultHash ? ` + ' ${n.defaultHash}'` : '';
4812
5034
  if(ctx.inuse.apply) {
4813
- 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})`);
4814
5037
  } else {
4815
- ctx.writeLine(`$runtime.setClassToElement(${n.el}, ${n.exp}${base});`);
5038
+ ctx.write(true, `$runtime.setClassToElement(${n.el}, ${n.exp}${base});`);
4816
5039
  }
4817
5040
  }
4818
5041
  });
5042
+ requireCD.$depends(bind);
4819
5043
  return {bind};
4820
5044
  } else {
4821
5045
  let bind = xNode('block');
@@ -4829,18 +5053,22 @@
4829
5053
  assert(className);
4830
5054
  let exp = prop.value ? unwrapExp(prop.value) : className;
4831
5055
  this.detectDependency(exp);
4832
- bind.push(xNode('bindClass', {
5056
+
5057
+ let n = xNode('bindClass', {
5058
+ $deps: [this.glob.apply],
4833
5059
  el: element.bindName(),
4834
5060
  className,
4835
5061
  exp,
4836
- $element: exp.includes('$element')
5062
+ $element: exp.includes('$element'),
5063
+ requireCD
4837
5064
  }, (ctx, n) => {
4838
5065
  if(n.$element) {
4839
5066
  ctx.writeLine(`{`);
4840
5067
  ctx.indent++;
4841
5068
  ctx.writeLine(`let $element = ${n.el};`);
4842
5069
  }
4843
- if(ctx.inuse.apply) {
5070
+ if(this.glob.apply.value) {
5071
+ n.requireCD.$value(true);
4844
5072
  ctx.writeLine(`$runtime.bindClass($cd, ${n.el}, () => !!(${n.exp}), '${n.className}');`);
4845
5073
  } else {
4846
5074
  ctx.writeLine(`(${n.exp}) && $runtime.addClass(${n.el}, '${n.className}');`);
@@ -4849,18 +5077,22 @@
4849
5077
  ctx.indent--;
4850
5078
  ctx.writeLine(`}`);
4851
5079
  }
4852
- }));
5080
+ });
5081
+ requireCD.$depends(n);
5082
+ bind.push(n);
4853
5083
  }
4854
5084
  });
4855
5085
  return {bind: bind.body.length ? bind : null};
4856
5086
  }
4857
5087
  } else if(name[0] == '^') {
4858
- this.require('$cd');
5088
+ requireCD.$value(true);
4859
5089
  return {bindTail: xNode('bindAnchor', {
4860
5090
  name: name.slice(1) || 'default',
4861
5091
  el: element.bindName()
4862
5092
  }, (ctx, n) => {
4863
- 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}');`);
4864
5096
  })};
4865
5097
  } else {
4866
5098
  if(prop.value && prop.value.indexOf('{') >= 0) {
@@ -4879,36 +5111,41 @@
4879
5111
  selected: true,
4880
5112
  innerHTML: true,
4881
5113
  innerText: true,
5114
+ placeholder: true,
4882
5115
  src: true,
4883
5116
  readonly: 'readOnly'
4884
5117
  };
4885
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
+
4886
5146
  return {bind: xNode('block', {
4887
5147
  scope: hasElement,
4888
- body: [
4889
- xNode('bindAttribute', {
4890
- name,
4891
- exp,
4892
- hasElement,
4893
- el: element.bindName()
4894
- }, (ctx, data) => {
4895
- if(data.hasElement) ctx.writeLine(`let $element=${data.el};`);
4896
- if(propList[name]) {
4897
- let propName = propList[name] === true ? name : propList[name];
4898
- if(ctx.inuse.apply) {
4899
- ctx.writeLine(`$watchReadOnly($cd, () => (${data.exp}), (value) => {${data.el}.${propName} = value;});`);
4900
- } else {
4901
- ctx.writeLine(`${data.el}.${propName} = ${data.exp};`);
4902
- }
4903
- } else {
4904
- if(ctx.inuse.apply) {
4905
- ctx.writeLine(`$runtime.bindAttribute($cd, ${data.el}, '${data.name}', () => (${data.exp}));`);
4906
- } else {
4907
- ctx.writeLine(`$runtime.bindAttributeBase(${data.el}, '${data.name}', ${data.exp});`);
4908
- }
4909
- }
4910
- })
4911
- ]
5148
+ body: [n]
4912
5149
  })};
4913
5150
  }
4914
5151
 
@@ -4921,104 +5158,86 @@
4921
5158
  }
4922
5159
  }
4923
5160
 
4924
- function makeifBlock(data, element) {
4925
- let r = data.value.match(/^#if (.*)$/s);
5161
+ function makeifBlock(data, element, requireCD) {
5162
+ let r = data.value.match(/^#if (.*)$/);
4926
5163
  let exp = r[1];
4927
5164
  assert(exp, 'Wrong binding: ' + data.value);
4928
5165
  this.detectDependency(exp);
4929
5166
  this.require('$cd');
4930
5167
 
4931
- let mainBlock, elseBlock, mainTpl, elseTpl;
5168
+ let mainBlock, elseBlock;
4932
5169
 
4933
- if(data.bodyMain) {
4934
- mainBlock = this.buildBlock({body: data.bodyMain}, {protectLastTag: true, inline: true});
4935
- 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
+ };
4936
5181
 
4937
- elseTpl = xNode('template', {
4938
- inline: true,
4939
- body: elseBlock.tpl,
4940
- svg: elseBlock.svg
4941
- });
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}));
4942
5185
  } else {
4943
- mainBlock = this.buildBlock(data, {protectLastTag: true, inline: true});
5186
+ mainBlock = getBlock(this.buildBlock(data, {protectLastTag: true, allowSingleBlock: true}));
4944
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
+ }
4945
5204
 
4946
- mainTpl = xNode('template', {
4947
- inline: true,
4948
- body: mainBlock.tpl,
4949
- svg: mainBlock.svg
4950
- });
4951
-
4952
- const source = xNode('if:bind', {
4953
- el: element.bindName(),
4954
- exp,
4955
- mainTpl,
4956
- mainBlock: mainBlock.source,
4957
- elseTpl,
4958
- elseBlock: elseBlock && elseBlock.source
4959
- },
4960
- (ctx, data) => {
4961
- ctx.writeLine(`$runtime.$$ifBlock($cd, ${data.el}, () => !!(${data.exp}),`);
4962
5205
  ctx.indent++;
4963
- ctx.writeIndent();
4964
- ctx.build(data.mainTpl);
4965
- ctx.write(',\n');
4966
- ctx.writeIndent();
4967
- if(data.mainBlock) {
4968
- ctx.build(xNode('function', {
4969
- inline: true,
4970
- arrow: true,
4971
- args: ['$cd', '$parentElement'],
4972
- body: [data.mainBlock]
4973
- }));
4974
- } else ctx.write('$runtime.noop');
4975
- if(data.elseTpl) {
4976
- ctx.write(',\n');
4977
- ctx.writeIndent();
4978
- ctx.build(data.elseTpl);
4979
- ctx.write(',\n');
4980
- ctx.writeIndent();
4981
- if(data.elseBlock) {
4982
- ctx.build(xNode('function', {
4983
- inline: true,
4984
- arrow: true,
4985
- args: ['$cd', '$parentElement'],
4986
- body: [data.elseBlock]
4987
- }));
4988
- } 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);
4989
5211
  }
4990
5212
  ctx.indent--;
4991
- ctx.write(');\n');
5213
+ ctx.write(true, ');', true);
4992
5214
  });
4993
-
4994
- return {source};
5215
+ requireCD.$depends(result);
5216
+ this.glob.component.$depends(result);
5217
+ return result;
4995
5218
  }
4996
5219
 
4997
5220
  function makeEachBlock(data, option) {
4998
-
4999
- let nodeItems = trimEmptyNodes(data.body);
5000
- if(!nodeItems.length) nodeItems = [data.body[0]];
5001
-
5002
- let itemData = this.buildBlock({body: nodeItems}, {protectLastTag: true, inline: true});
5221
+ this.require('apply');
5003
5222
 
5004
5223
  // #each items as item, index (key)
5005
- let rx = data.value.match(/^#each\s+(.+)\s+as\s+(.+)$/s);
5224
+ let rx = data.value.match(/^#each\s+(.+)\s+as\s+(.+)$/);
5006
5225
  assert(rx, `Wrong #each expression '${data.value}'`);
5007
5226
  let arrayName = rx[1];
5008
5227
  let right = rx[2];
5009
5228
  let keyName;
5010
5229
 
5011
5230
  // get keyName
5012
- rx = right.match(/^(.*)\s*\(\s*([^\(\)]+)\s*\)\s*$/s);
5231
+ rx = right.match(/^(.*)\s*\(\s*([^\(\)]+)\s*\)\s*$/);
5013
5232
  if(rx) {
5014
5233
  right = rx[1];
5015
5234
  keyName = rx[2];
5016
5235
  }
5017
5236
  right = right.trim();
5018
5237
 
5019
- let itemName, indexName, bind0 = null;
5238
+ let itemName, indexName, blockPrefix = null;
5020
5239
  if(right[0] == '{') {
5021
- rx = right.match(/^(\{[^}]+\})(.*)$/s);
5240
+ rx = right.match(/^(\{[^}]+\})(.*)$/);
5022
5241
  assert(rx, `Wrong #each expression '${data.value}'`);
5023
5242
  let exp = rx[1], keywords;
5024
5243
 
@@ -5033,12 +5252,15 @@
5033
5252
  if(indexName[0] == ',') indexName = indexName.substring(1).trim();
5034
5253
  indexName = indexName || '$index';
5035
5254
 
5036
- bind0 = xNode('each:unwrap', {
5255
+ blockPrefix = xNode('each:unwrap', {
5037
5256
  exp,
5038
5257
  keywords
5039
5258
  }, (ctx, n) => {
5040
- ctx.writeLine(`let ${n.keywords.join(', ')};`);
5041
- 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
+ }
5042
5264
  });
5043
5265
  } else {
5044
5266
  rx = right.trim().split(/\s*\,\s*/);
@@ -5071,82 +5293,93 @@
5071
5293
  })]
5072
5294
  });
5073
5295
  }
5074
- let bind;
5075
- if(itemData.source) {
5076
- bind = xNode('function', {
5077
- inline: true,
5078
- arrow: true,
5079
- args: ['$ctx', '$parentElement', itemName, indexName],
5080
- body: [
5081
- `let $cd = $ctx.cd;`,
5082
- bind0,
5083
- itemData.source,
5084
- xNode(ctx => {
5085
- ctx.writeLine(`$ctx.rebind = function(_${indexName}, _${itemName}) {`);
5086
- ctx.indent++;
5087
- ctx.writeLine(`${indexName} = _${indexName};`);
5088
- ctx.writeLine(`${itemName} = _${itemName};`);
5089
- ctx.indent--;
5090
- ctx.writeLine(`};`);
5091
- })
5092
- ]
5093
- });
5094
- } else {
5095
- bind = xNode('function', {
5096
- inline: true,
5097
- arrow: true,
5098
- args: ['$ctx'],
5099
- 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};}`);
5100
5303
  });
5101
5304
  }
5102
5305
 
5103
- const template = xNode('template', {
5104
- inline: true,
5105
- body: itemData.tpl,
5106
- 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
+ }
5107
5318
  });
5108
5319
 
5109
- 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
+
5110
5338
  const source = xNode('each', {
5111
5339
  keyFunction,
5112
- template,
5113
- bind
5114
- }, (ctx, data) => {
5340
+ block: itemBlock,
5341
+ }, (ctx, n) => {
5115
5342
  ctx.writeLine(`$runtime.$$eachBlock($cd, ${option.elName}, ${option.onlyChild?1:0}, () => (${arrayName}),`);
5116
5343
  ctx.indent++;
5117
- ctx.writeIndent();
5118
- if(data.keyFunction === 'noop') ctx.write('$runtime.noop');
5119
- 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);
5120
5347
  else ctx.write('$runtime.eachDefaultKey');
5121
- ctx.write(`,\n`);
5122
- ctx.writeIndent();
5123
- ctx.build(data.template);
5124
- ctx.write(`,\n`);
5125
- ctx.writeIndent();
5126
- ctx.build(data.bind);
5127
- ctx.write(`);\n`);
5348
+ ctx.write(`,`);
5349
+ ctx.add(n.block);
5128
5350
  ctx.indent--;
5351
+ ctx.write(true, `);`, true);
5129
5352
  });
5130
5353
  this.detectDependency(arrayName);
5131
5354
 
5132
5355
  return {source};
5133
5356
  }
5134
5357
 
5135
- function makeHtmlBlock(exp, label) {
5358
+ function makeHtmlBlock(exp, label, requireCD) {
5136
5359
  this.detectDependency(exp);
5137
5360
  this.require('$cd');
5138
- return xNode('block', {
5361
+ const result = xNode('block', {
5362
+ $deps: [this.glob.apply],
5139
5363
  el: label.bindName(),
5140
- exp
5364
+ exp,
5365
+ requireCD
5141
5366
  }, (ctx, n) => {
5142
- 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}));`);
5143
5373
  });
5374
+
5375
+ requireCD.$depends(result);
5376
+ return result;
5144
5377
  }
5145
5378
 
5146
5379
  function makeAwaitBlock(node, element) {
5147
5380
  let valueForThen, exp;
5148
5381
 
5149
- 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*$/);
5150
5383
  if(rx) {
5151
5384
  assert(!node.parts.then);
5152
5385
  node.parts.then = node.parts.main;
@@ -5154,7 +5387,7 @@
5154
5387
  exp = rx[1];
5155
5388
  valueForThen = rx[2];
5156
5389
  } else {
5157
- rx = node.value.match(/^#await\s+(.+)\s*$/s);
5390
+ rx = node.value.match(/^#await\s+(.+)\s*$/);
5158
5391
  assert(rx);
5159
5392
  exp = rx[1].trim();
5160
5393
  }
@@ -5163,7 +5396,7 @@
5163
5396
 
5164
5397
  let parts = [null, null, null];
5165
5398
  if(node.parts.main && node.parts.main.length) {
5166
- parts[0] = this.buildBlock({body: node.parts.main}, {protectLastTag: true, inlineFunction: true});
5399
+ parts[0] = this.buildBlock({body: node.parts.main}, {protectLastTag: true});
5167
5400
  }
5168
5401
  if(node.parts.then && node.parts.then.length) {
5169
5402
  let args = [];
@@ -5171,27 +5404,30 @@
5171
5404
  assert(isSimpleName(valueForThen));
5172
5405
  args.push(valueForThen);
5173
5406
  } else {
5174
- let rx = node.parts.thenValue.match(/^[^ ]+\s+(.*)$/s);
5407
+ let rx = node.parts.thenValue.match(/^[^ ]+\s+(.*)$/);
5175
5408
  if(rx) {
5176
5409
  assert(isSimpleName(rx[1]));
5177
5410
  args.push(rx[1]);
5178
5411
  }
5179
5412
  }
5180
- 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});
5181
5414
  }
5182
5415
  if(node.parts.catch && node.parts.catch.length) {
5183
5416
  let args = [];
5184
- let rx = node.parts.catchValue.match(/^[^ ]+\s+(.*)$/s);
5417
+ let rx = node.parts.catchValue.match(/^[^ ]+\s+(.*)$/);
5185
5418
  if(rx) {
5186
5419
  assert(isSimpleName(rx[1]));
5187
5420
  args.push(rx[1]);
5188
5421
  }
5189
- 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});
5190
5423
  }
5191
5424
 
5425
+ if(this.script.readOnly) {
5426
+ this.warning('script read-only conflicts with await');
5427
+ return;
5428
+ }
5192
5429
  this.detectDependency(exp);
5193
- if(this.script.readOnly) this.warning('script read-only conflicts with await');
5194
- this.require('apply', '$cd');
5430
+ this.require('apply');
5195
5431
 
5196
5432
  return xNode('await', {
5197
5433
  el: element.bindName(),
@@ -5199,127 +5435,88 @@
5199
5435
  parts,
5200
5436
  keywords
5201
5437
  }, (ctx, n) => {
5202
- ctx.writeIndent();
5203
- ctx.write(`$runtime.$$awaitBlock($cd, ${n.el}, () => [${n.keywords.join(', ')}], () => ${n.exp}, $$apply,\n`);
5204
- ctx.goIndent(() => {
5205
- n.parts.forEach((part, index) => {
5206
- if(part) {
5207
- let {source, tpl, svg} = part;
5208
- ctx.writeIndent();
5209
- if(source) {
5210
- ctx.build(source);
5211
- ctx.write(',\n');
5212
- ctx.writeIndent();
5213
- } else ctx.write(`$runtime.noop, `);
5214
- ctx.build(xNode('template', {body: tpl, svg, inline: true}));
5215
- ctx.write(index == 2 ? '\n' : ',\n');
5216
- } else {
5217
- ctx.writeLine(`null, null` + (index == 2 ? '' : ','));
5218
- } });
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');
5219
5446
  });
5220
- ctx.writeLine(');');
5447
+ ctx.indent--;
5448
+ ctx.write(');', true);
5221
5449
  });
5222
5450
  }
5223
5451
 
5224
- function attachSlot(slotName, label, node) {
5225
- let props = [];
5452
+ function attachSlot(slotName, node, requireCD) {
5453
+ let props = [], staticProps = true;
5454
+
5226
5455
  if(node.attributes && node.attributes.length) {
5227
5456
  node.attributes.forEach(prop => {
5228
- let name = prop.name;
5229
- let value = prop.value;
5230
- if(name[0] == '{') {
5231
- assert(value == null);
5232
- value = name;
5233
- name = unwrapExp(name);
5234
- } assert(value != null);
5235
- assert(isSimpleName(name));
5236
- if(value[0] == '{') {
5237
- value = unwrapExp(value);
5238
- 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
+ }));
5239
5465
 
5240
- props.push(xNode('prop', {
5241
- name,
5242
- value,
5243
- dyn: true
5244
- }, (ctx, n) => {
5245
- if(this.inuse.apply) ctx.write(`${n.name}: () => (${n.value})`);
5246
- else ctx.write(`${n.name}: (${n.value})`);
5247
- }));
5248
- } else {
5249
- props.push(xNode('static-prop', {
5250
- name,
5251
- value
5252
- }, (ctx, n) => {
5253
- ctx.write(`${n.name}: \`${this.Q(n.value)}\``);
5254
- }));
5255
- }
5256
5466
  });
5257
5467
  }
5258
5468
  let placeholder;
5259
- if(node.body && node.body.length) {
5260
- let block = this.buildBlock(node, {inline: true});
5261
-
5262
- const tpl = xNode('template', {
5263
- name: '$parentElement',
5264
- body: block.tpl,
5265
- svg: block.svg
5266
- });
5469
+ if(node.body?.length) placeholder = this.buildBlock(node).block;
5267
5470
 
5268
- placeholder = xNode('placeholder', {
5269
- el: label.bindName(),
5270
- body: block.source,
5271
- tpl
5272
- }, (ctx, n) => {
5273
- ctx.build(n.tpl);
5274
- ctx.build(n.body);
5275
- ctx.writeLine(`$runtime.insertAfter(${n.el}, $parentElement);`);
5276
- });
5277
- }
5278
-
5279
- this.require('$component', '$cd', '$context');
5471
+ this.require('$context');
5472
+ this.glob.component.$value(true);
5280
5473
 
5281
- return xNode('slot', {
5474
+ let result = xNode('slot', {
5475
+ $deps: [this.glob.apply],
5282
5476
  name: slotName,
5283
- el: label.bindName(),
5284
5477
  props,
5285
- placeholder
5478
+ staticProps,
5479
+ placeholder,
5480
+ requireCD
5286
5481
  }, (ctx, n) => {
5287
- let hasDynProps = n.props.some(p => p.dyn);
5288
- let base = 'Base';
5289
- if(hasDynProps && ctx.inuse.apply) {
5290
- assert(!ctx._ctx.script.readOnly);
5291
- base = '';
5292
- }
5293
- 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
+
5294
5490
  if(n.props.length) {
5295
- ctx.write(`{\n`);
5296
- ctx.goIndent(() => {
5297
- for(let i=0; i < n.props.length; i++) {
5298
- let prop = n.props[i];
5299
- ctx.writeIndent();
5300
- ctx.build(prop);
5301
- if(i + 1 < n.props.length) ctx.write(',');
5302
- ctx.write('\n');
5303
- }
5491
+ if(dynamicProps) ctx.write(', () => ({');
5492
+ else ctx.write(', {');
5493
+ n.props.forEach((prop, i) => {
5494
+ if(i) ctx.write(', ');
5495
+ ctx.add(prop);
5304
5496
  });
5305
- ctx.write(true, `}`);
5306
- } else {
5307
- ctx.write(`null`);
5308
- }
5497
+ ctx.write('}');
5498
+ if(dynamicProps) ctx.write(')');
5499
+ } else missed += ', null';
5500
+
5309
5501
  if(n.placeholder) {
5310
- ctx.write(', () => {\n');
5311
- ctx.goIndent(() => {
5312
- ctx.build(n.placeholder);
5313
- });
5314
- ctx.write(true, '}');
5315
- } else if(hasDynProps && !this.config.immutable) ctx.write(`, 0`);
5316
- if(hasDynProps && !this.config.immutable) ctx.write(`, $runtime.$$compareDeep`);
5317
- 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(')');
5318
5513
  });
5514
+ requireCD.$depends(result);
5515
+ return result;
5319
5516
  }
5320
5517
 
5321
- function makeFragment(node) {
5322
- let rx = node.value.match(/#fragment\:(\S+)(.*)$/s);
5518
+ function makeFragment(node, requireCD) {
5519
+ let rx = node.value.match(/#fragment\:(\S+)(.*)$/);
5323
5520
  assert(rx);
5324
5521
  let name = rx[1], external = false;
5325
5522
  assert(isSimpleName(name));
@@ -5334,9 +5531,12 @@
5334
5531
  });
5335
5532
  }
5336
5533
 
5534
+ if(external) requireCD.$value(true);
5535
+
5337
5536
  let block;
5338
- if(node.body && node.body.length) block = this.buildBlock({body: trimEmptyNodes(node.body)}, {inline: true, context: 'fragment'});
5339
- else {
5537
+ if(node.body && node.body.length) {
5538
+ block = this.buildBlock({body: trimEmptyNodes(node.body)}, {inline: true, context: 'fragment', parentElement: '$dom'});
5539
+ } else {
5340
5540
  this.warning(`Empty fragment: '${node.value}'`);
5341
5541
  return xNode('empty-fragment', {name}, (ctx, n) => {
5342
5542
  ctx.writeLine(`function $fragment_${n.name}() {};`);
@@ -5344,36 +5544,45 @@
5344
5544
  }
5345
5545
 
5346
5546
  return xNode('fragment', {
5547
+ $compile: [block.source, this.glob.apply],
5548
+ $deps: [block.requireCD],
5347
5549
  name,
5348
5550
  props,
5349
5551
  external,
5350
- source: block.source,
5351
- template: xNode('template', {
5352
- name: '$parentElement',
5353
- body: block.tpl,
5354
- svg: block.svg
5355
- })
5552
+ block
5356
5553
  }, (ctx, n) => {
5357
- ctx.write(true, `function $fragment_${n.name}($cd, $$label, $props, $events, $$fragmentSlot) {\n`);
5358
- ctx.indent++;
5359
-
5360
- if(n.props?.length) {
5361
- if(ctx.inuse.apply) {
5362
- ctx.writeLine('let ' + n.props.join(', ') + ';');
5363
- ctx.writeLine(`$runtime.unwrapProps($cd, $props, ($$) => ({${n.props.join(', ')}} = $$));`);
5364
- } else {
5365
- ctx.writeLine('let ' + n.props.join(', ') + ';');
5366
- 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
+ }
5367
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('}');
5368
5584
  }
5369
-
5370
- ctx.build(n.template);
5371
- ctx.build(n.source);
5372
- ctx.writeLine(`$runtime.insertAfter($$label, $parentElement);`);
5373
-
5374
- ctx.indent--;
5375
- ctx.writeLine('}');
5376
- 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});`);
5377
5586
  });
5378
5587
  }
5379
5588
 
@@ -5395,7 +5604,7 @@
5395
5604
  name = name.substring(2);
5396
5605
  events.push({
5397
5606
  name,
5398
- callback: `$events?.${name}`
5607
+ callback: `$events.${name}`
5399
5608
  });
5400
5609
  }
5401
5610
  return;
@@ -5414,64 +5623,48 @@
5414
5623
  }
5415
5624
 
5416
5625
 
5417
- function attachFragment(node, element) {
5626
+ function attachFragment(node) {
5418
5627
  let name = node.elArg;
5419
5628
  assert(isSimpleName(name));
5420
5629
 
5421
- let slotBlock = null;
5422
-
5423
- if(node.body?.length) slotBlock = this.buildBlock({body: trimEmptyNodes(node.body)}, {inline: true});
5424
-
5425
- let {props, events, forwardAllEvents, staticProps} = parseAttibutes.call(this, node.attributes);
5426
- this.require('$cd');
5427
-
5428
5630
  let slot = null;
5429
- if(slotBlock) {
5430
- let template = xNode('template', {
5431
- name: '$parentElement',
5432
- body: slotBlock.tpl,
5433
- svg: slotBlock.svg,
5434
- inline: !slotBlock.source
5435
- });
5631
+ if(node.body?.length) slot = this.buildBlock({body: trimEmptyNodes(node.body)}, {inline: true});
5436
5632
 
5437
- slot = {
5438
- source: slotBlock.source,
5439
- template
5440
- };
5441
- }
5633
+ let {props, events, forwardAllEvents, staticProps} = parseAttibutes.call(this, node.attributes);
5442
5634
 
5443
5635
  return xNode('call-fragment', {
5636
+ $compile: [slot?.source],
5637
+ $deps: [this.glob.apply],
5444
5638
  forwardAllEvents,
5445
- el: element.bindName(),
5446
5639
  name,
5447
5640
  events,
5448
5641
  props,
5449
5642
  slot,
5450
5643
  staticProps
5451
5644
  }, (ctx, n) => {
5452
- ctx.write(true, `$fragment_${n.name}($cd, ${n.el}`);
5645
+ ctx.write(`$fragment_${n.name}(`);
5453
5646
  let missed = '';
5454
5647
  ctx.indent++;
5455
5648
 
5456
5649
  if(n.props.length) {
5457
- ctx.write(',\n', true);
5650
+ ctx.write(true);
5458
5651
 
5459
5652
  const writeProps = () => ctx.write('{' + n.props.map(p => p.name == p.value ? p.name : `${p.name}: ${p.value}`).join(', ') + '}');
5460
5653
 
5461
- if(n.staticProps) writeProps();
5654
+ if(n.staticProps || !this.glob.apply.value) writeProps();
5462
5655
  else {
5463
5656
  ctx.write(`() => (`);
5464
5657
  writeProps();
5465
5658
  ctx.write(`)`);
5466
5659
  }
5467
- } else missed = ', 0';
5660
+ } else missed = 'null';
5468
5661
 
5469
5662
  if(n.forwardAllEvents) {
5470
5663
  if(n.events.length) this.warning(`Fragment: mixing binding and forwarding is not supported: '${node.openTag}'`);
5471
5664
  ctx.write(missed, ', $events');
5472
5665
  missed = '';
5473
5666
  } else if(n.events.length) {
5474
- ctx.write(missed, ',\n', true, '{');
5667
+ ctx.write(missed, ',', true, '{');
5475
5668
  missed = '';
5476
5669
 
5477
5670
  n.events.forEach((e, i) => {
@@ -5489,42 +5682,42 @@
5489
5682
  } else missed += ', 0';
5490
5683
 
5491
5684
  if(n.slot) {
5492
- ctx.write(missed, ',\n');
5685
+ ctx.write(missed, ',', true);
5493
5686
  missed = '';
5494
- if(n.slot.source) {
5495
- ctx.writeLine(`($cd, $$label) => {`);
5496
- ctx.goIndent(() => {
5497
- ctx.build(n.slot.template);
5498
- ctx.build(n.slot.source);
5499
- ctx.writeLine(`$runtime.insertAfter($$label, $parentElement);`);
5500
- });
5501
- ctx.write(true, `}`);
5687
+ if(ctx.isEmpty(n.slot.source)) {
5688
+ ctx.write(`$runtime.makeStaticBlock(`);
5689
+ ctx.add(n.slot.template);
5690
+ ctx.write(`)`);
5502
5691
  } else {
5503
- ctx.write(true, `($cd, $$label) => $runtime.insertAfter($$label, `);
5504
- ctx.build(n.slot.template);
5505
- 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, `})`);
5506
5699
  }
5507
5700
  }
5508
5701
 
5509
5702
  ctx.indent--;
5510
- if(n.props.length || n.events.length || n.slot) ctx.write(true, ');\n');
5511
- else ctx.write(');\n');
5703
+ if(n.props.length || n.events.length || n.slot) ctx.write(true, ')');
5704
+ else ctx.write(')');
5512
5705
 
5513
5706
  });
5514
5707
  }
5515
5708
 
5516
- function attachFragmentSlot(label) {
5517
- this.require('$cd');
5709
+ function attachFragmentSlot(label, requireCD) {
5710
+ requireCD.$value(true);
5518
5711
 
5519
5712
  return xNode('fragment-slot', {
5520
5713
  el: label.bindName()
5521
5714
  }, (ctx, n) => {
5522
- ctx.writeLine(`$$fragmentSlot?.($cd, ${n.el});`);
5715
+ ctx.write(true, `$runtime.attachBlock($cd, ${n.el}, $$fragmentSlot?.())`);
5523
5716
  });
5524
5717
  }
5525
5718
 
5526
- function attchExportedFragment(node, label, componentName) {
5527
- this.require('$cd');
5719
+ function attchExportedFragment(node, label, componentName, requireCD) {
5720
+ requireCD.$value(true);
5528
5721
 
5529
5722
  let data = {
5530
5723
  name: node.elArg,
@@ -5534,48 +5727,44 @@
5534
5727
 
5535
5728
  let body = trimEmptyNodes(node.body || []);
5536
5729
  if(body.length) {
5537
- let block = this.buildBlock({body}, {inline: true});
5538
- assert(!block.svg, 'SVG is not supported for exported fragment');
5539
- data.source = block.source;
5540
- data.template = xNode('template', {
5541
- raw: true,
5542
- body: block.tpl
5543
- });
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');
5544
5734
  }
5545
5735
 
5546
5736
  let pa = parseAttibutes.call(this, node.attributes);
5547
- data.props = pa.props;
5548
- data.events = pa.events;
5549
- data.forwardAllEvents = pa.forwardAllEvents;
5550
- data.staticProps = pa.staticProps;
5737
+ data = {...pa, ...data};
5551
5738
 
5552
5739
  return xNode('attach-exported-fragment', data, (ctx, n) => {
5553
- ctx.write(true, `$runtime.attchExportedFragment($cd, $instance_${n.componentName}, '${n.name}', ${n.label}`);
5554
- let missed = '';
5740
+ ctx.write(true, `$runtime.attachBlock($cd, ${n.label}, $runtime.callExportedFragment($instance_${n.componentName}, '${n.name}'`);
5555
5741
  ctx.indent++;
5742
+ let missed = '';
5556
5743
 
5557
- if(n.props.length) {
5558
- ctx.write(',\n', true);
5559
-
5560
- 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);
5561
5746
 
5562
- if(n.staticProps) writeProps();
5563
- else {
5564
- ctx.write(`$runtime.observeProps(`);
5565
- if(this.config.immutable) ctx.write(`$runtime.keyComparator`);
5566
- else ctx.write(`$runtime.$$compareDeep`);
5567
- ctx.write(', () => (');
5568
- writeProps();
5569
- 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, `})`);
5570
5759
  }
5571
- } else missed = ', 0';
5760
+ } else missed = ', null';
5572
5761
 
5573
5762
  if(n.forwardAllEvents) {
5574
5763
  if(n.events.length) this.warning(`Fragment: mixing binding and forwarding is not supported: '${node.openTag}'`);
5575
5764
  ctx.write(missed, ', $events');
5576
5765
  missed = '';
5577
5766
  } else if(n.events.length) {
5578
- ctx.write(missed, ',\n', true, '{');
5767
+ ctx.write(missed, ',', true, '{');
5579
5768
  missed = '';
5580
5769
 
5581
5770
  n.events.forEach((e, i) => {
@@ -5590,27 +5779,32 @@
5590
5779
  }
5591
5780
  });
5592
5781
  ctx.write('}');
5593
- } else missed += ', 0';
5782
+ } else missed += ', null';
5594
5783
 
5595
- if(n.template) {
5596
- if(missed) ctx.write(missed, `, \``);
5597
- else ctx.write(`,\n`, true, `\``);
5598
- ctx.build(n.template);
5599
- if(n.source) {
5600
- ctx.write(`\`, ($cd, $parentElement) => {\n`);
5601
- ctx.indent++;
5602
- ctx.build(n.source);
5603
- ctx.indent--;
5604
- ctx.writeLine(`});`);
5605
- } else {
5606
- 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`);
5607
5798
  }
5608
- } else ctx.write(');\n');
5799
+
5800
+ }
5801
+
5609
5802
  ctx.indent--;
5803
+ ctx.write('));');
5610
5804
  });
5611
5805
  }
5612
5806
 
5613
- function attachHead(n) {
5807
+ function attachHead(n, requireCD) {
5614
5808
  if(n.elArg == 'window' || n.elArg == 'body') {
5615
5809
  let name = 'el' + (this.uniqIndex++);
5616
5810
  let block = this.buildBlock({body: [n]}, {malinaElement: true, inline: true, oneElement: name, bindAttributes: true});
@@ -5636,7 +5830,10 @@
5636
5830
  return true;
5637
5831
  });
5638
5832
 
5639
- let d = {};
5833
+ let d = {
5834
+ $deps: [this.glob.apply],
5835
+ requireCD
5836
+ };
5640
5837
  if(title?.body?.[0]) {
5641
5838
  assert(title.body[0].type == 'text');
5642
5839
  let r = this.parseText(title.body[0].value);
@@ -5647,27 +5844,39 @@
5647
5844
  }
5648
5845
  }
5649
5846
  if(body.length) {
5650
- let block = this.buildBlock({body}, {inline: true});
5651
- d.source = block.source;
5652
- d.template = xNode('template', {
5653
- name: '$parentElement',
5654
- body: block.tpl
5847
+ let bb = this.buildBlock({body}, {
5848
+ inline: true,
5849
+ template: {
5850
+ name: '$parentElement',
5851
+ cloneNode: true,
5852
+ requireFragment: true
5853
+ }
5655
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
+
5656
5862
  this.require('$onDestroy');
5657
5863
  }
5658
5864
 
5659
- 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);
5660
5867
  if(n.title != null) ctx.writeLine(`document.title = ${n.title};`);
5661
5868
  if(n.dynTitle) {
5662
- if(ctx.inuse.apply) ctx.writeLine(`$watchReadOnly($cd, () => (${n.dynTitle}), (value) => {document.title = value;});`);
5663
- 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};`);
5664
5873
  }
5665
5874
 
5666
5875
  if(n.template) {
5667
5876
  ctx.writeLine(`{`);
5668
5877
  ctx.indent++;
5669
- ctx.build(n.template);
5670
- ctx.build(n.source);
5878
+ ctx.add(n.template);
5879
+ ctx.add(n.source);
5671
5880
  ctx.writeLine(`let a=$parentElement.firstChild, b=$parentElement.lastChild;`);
5672
5881
  ctx.writeLine(`$onDestroy(() => {$runtime.$$removeElements(a, b)});`);
5673
5882
  ctx.writeLine(`document.head.appendChild($parentElement);`);
@@ -5675,6 +5884,8 @@
5675
5884
  ctx.writeLine(`}`);
5676
5885
  }
5677
5886
  });
5887
+ requireCD.$depends(result);
5888
+ return result;
5678
5889
  } else throw 'Wrong tag';
5679
5890
  }
5680
5891
 
@@ -5728,37 +5939,47 @@
5728
5939
  return {name, value, rawValue, static: statical};
5729
5940
  }
5730
5941
 
5731
- function attachPortal(node) {
5942
+ function attachPortal(node, requireCD) {
5732
5943
  let body = trimEmptyNodes(node.body || []);
5733
5944
  if(!body.length) return;
5734
- 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');
5735
5956
 
5736
5957
  let mount = node.attributes.find(a => a.name == 'mount')?.value;
5737
5958
  if(mount) mount = unwrapExp(mount);
5738
5959
 
5739
- this.require('$cd');
5740
-
5741
- return xNode('portal', {
5960
+ const result = xNode('portal', {
5961
+ $compile: [bb.source],
5962
+ $deps: [bb.requireCD],
5742
5963
  mount,
5743
- source: block.source,
5744
- template: xNode('template', {
5745
- name: '$parentElement',
5746
- body: block.tpl,
5747
- svg: block.svg
5748
- })
5964
+ source: bb.source,
5965
+ template: bb.template,
5966
+ requireCD
5749
5967
  }, (ctx, n) => {
5968
+ if(n.$deps[0].value) n.requireCD.$value(true);
5750
5969
  let label = n.mount || 'document.body';
5751
5970
  ctx.writeLine('{');
5752
5971
  ctx.indent++;
5753
- ctx.build(n.template);
5754
- ctx.build(n.source);
5972
+ ctx.add(n.template);
5973
+ ctx.add(n.source);
5755
5974
  ctx.writeLine(`let $$first = $parentElement[$runtime.firstChild];`);
5756
5975
  ctx.writeLine(`let $$last = $parentElement.lastChild;`);
5757
- 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));`);
5758
5977
  ctx.writeLine(`$tick(() => ${label}.appendChild($parentElement));`);
5759
5978
  ctx.indent--;
5760
5979
  ctx.writeLine('}');
5761
5980
  });
5981
+ requireCD.$depends(result);
5982
+ return result;
5762
5983
  }
5763
5984
 
5764
5985
  function makeEventProp(prop, requireElement) {
@@ -5781,6 +6002,7 @@
5781
6002
  let modList = [], _mod = '';
5782
6003
  let handler = '', exp, func;
5783
6004
  let step = 0;
6005
+ let rootModifier = false;
5784
6006
  for(let a of name) {
5785
6007
  if(a == '|') {
5786
6008
  assert$1(step <= 1);
@@ -5808,14 +6030,18 @@
5808
6030
 
5809
6031
  this.detectDependency(exp || handler);
5810
6032
 
6033
+ let globalFunction = false;
5811
6034
  if(exp) {
5812
6035
  let type = detectExpressionType(exp);
5813
6036
  if(type == 'identifier') {
6037
+ globalFunction = !!this.script.rootFunctions[exp];
5814
6038
  handler = exp;
5815
6039
  exp = null;
5816
6040
  } else if(type == 'function') {
5817
6041
  func = exp;
5818
6042
  exp = null;
6043
+ } else if(type?.type == 'function-call') {
6044
+ globalFunction = !!this.script.rootFunctions[type.name];
5819
6045
  } }
5820
6046
 
5821
6047
  // modifiers
@@ -5836,6 +6062,10 @@
5836
6062
  let mods = [];
5837
6063
  let preventInserted;
5838
6064
  modList.forEach(opt => {
6065
+ if(opt == 'root') {
6066
+ rootModifier = true;
6067
+ return;
6068
+ }
5839
6069
  if(opt == 'preventDefault' || opt == 'prevent') {
5840
6070
  if(preventInserted) return;
5841
6071
  mods.push('$event.preventDefault();');
@@ -5874,7 +6104,8 @@
5874
6104
  exp,
5875
6105
  handlerName: handler,
5876
6106
  func,
5877
- mods
6107
+ mods,
6108
+ globalFunction
5878
6109
  }, (ctx, n) => {
5879
6110
  if(n.handlerName && !ctx.inuse.apply && !n.mods) return ctx.write(n.handlerName);
5880
6111
  ctx.write(`($event) => { `);
@@ -5884,14 +6115,14 @@
5884
6115
  if(last(n.exp) != ';') n.exp += ';';
5885
6116
  ctx.write(`${n.exp}`);
5886
6117
  } else if(n.func) ctx.write(`(${n.func})($event);`);
5887
- if(ctx.inuse.apply) ctx.write(` $$apply();`);
6118
+ if(ctx.inuse.apply && !n.globalFunction) ctx.write(` $$apply();`);
5888
6119
  ctx.write(`}`);
5889
6120
  });
5890
6121
 
5891
- return {event, fn};
6122
+ return {event, fn, rootModifier};
5892
6123
  }
5893
6124
 
5894
- const version = '0.6.52';
6125
+ const version = '0.7.0-alpha';
5895
6126
 
5896
6127
 
5897
6128
  async function compile(source, config = {}) {
@@ -5924,6 +6155,7 @@
5924
6155
  makeEachBlock,
5925
6156
  makeifBlock,
5926
6157
  makeComponent,
6158
+ makeComponentDyn,
5927
6159
  makeHtmlBlock,
5928
6160
  parseText,
5929
6161
  makeAwaitBlock,
@@ -5939,17 +6171,27 @@
5939
6171
  checkRootName: checkRootName,
5940
6172
 
5941
6173
  inuse: {},
5942
- require: function() {
5943
- 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) {
5944
6182
  let deps = true;
5945
6183
  if(name == '$props:no-deps') {name = '$props'; deps = false;} if(name == 'apply' && ctx.script.readOnly) name = 'blankApply';
5946
6184
  if(ctx.inuse[name] == null) ctx.inuse[name] = 0;
5947
6185
  ctx.inuse[name]++;
5948
6186
  if(!deps) continue;
6187
+ if(name == 'apply') ctx.glob.apply.$value(true);
6188
+ if(name == '$component') ctx.glob.component.$value(true);
5949
6189
  if(name == '$attributes') ctx.require('$props');
5950
6190
  if(name == '$props' && !ctx.script.readOnly) ctx.require('apply', '$cd');
5951
- if(name == 'apply' && !ctx.script.readOnly) ctx.require('$cd');
5952
- if(name == '$cd') ctx.require('$component');
6191
+ if(name == '$cd') {
6192
+ ctx.glob.rootCD.$value(true);
6193
+ ctx.require('$component');
6194
+ }
5953
6195
  if(name == '$onDestroy') ctx.require('$component');
5954
6196
  if(name == '$onMount') ctx.require('$component');
5955
6197
  }
@@ -5982,9 +6224,7 @@
5982
6224
  },
5983
6225
 
5984
6226
  xBuild: node => {
5985
- let w = new xWriter(ctx);
5986
- w.build(node);
5987
- return w.toString();
6227
+ return xBuild(ctx, node);
5988
6228
  }
5989
6229
  };
5990
6230
 
@@ -6036,48 +6276,9 @@
6036
6276
  result.push(`import { $$htmlToFragment } from 'malinajs/runtime.js';`);
6037
6277
  }
6038
6278
  result.push(ctx.module.top);
6279
+ result.push(makeComponentFn.call(ctx));
6039
6280
 
6040
- result.push(xNode('block', {
6041
- name: config.name,
6042
- body: [ctx.module.head, ctx.module.code, ctx.module.body]
6043
- }, (ctx, n) => {
6044
- ctx.writeIndent();
6045
- if(config.exportDefault) ctx.write('export default ');
6046
- else ctx.write(`const ${n.name} = `);
6047
-
6048
- let component = xNode('function', {
6049
- args: ['$option'],
6050
- inline: true,
6051
- arrow: true,
6052
- body: n.body
6053
- });
6054
-
6055
- if(ctx.inuse.apply) {
6056
- ctx.write('$runtime.makeComponent(');
6057
- component.args.push('$$apply');
6058
- ctx.build(component);
6059
- ctx.write(', $runtime.$base);\n');
6060
- } else if(ctx.inuse.$cd || ctx.inuse.$component || ctx.inuse.$context || ctx.inuse.blankApply) {
6061
- if(ctx.inuse.blankApply) {
6062
- component.body[0].body.unshift(xNode('block', (ctx) => {
6063
- ctx.writeLine('let $$apply = $runtime.noop;');
6064
- }));
6065
- }
6066
-
6067
- ctx.write('$runtime.makeComponent(');
6068
- ctx.build(component);
6069
- ctx.write(', $runtime.$readOnlyBase);\n');
6070
- } else {
6071
- ctx.write('($element, $option={}) => {\n');
6072
- ctx.goIndent(() => {
6073
- ctx.inuse.$insertElementByOption = true;
6074
- ctx.build(xNode('block', {body: n.body}));
6075
- });
6076
- ctx.write('}');
6077
- }
6078
- }));
6079
-
6080
- ctx.result = ctx.xBuild(result);
6281
+ ctx.result = xBuild(ctx, result);
6081
6282
 
6082
6283
  await hook(ctx, 'build');
6083
6284
  return ctx;
@@ -6126,8 +6327,7 @@
6126
6327
 
6127
6328
  if(localConfig) {
6128
6329
  const confFn = require(localConfig);
6129
- if(typeof(confFn) == 'function') result = confFn(result, filename);
6130
- else result = confFn;
6330
+ result = confFn(result, filename);
6131
6331
  }
6132
6332
  if(!result.path) result.path = filename;
6133
6333
  if(!result.name) result.name = filename.match(/([^/\\]+)\.\w+$/)[1];
@@ -6135,6 +6335,60 @@
6135
6335
  return result;
6136
6336
  }
6137
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
+
6138
6392
  exports.compile = compile;
6139
6393
  exports.version = version;
6140
6394