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