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