marko 4.28.0 → 4.28.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -480,6 +480,7 @@ class CustomTag extends HtmlElement {
480
480
  let tagVar = codegen.addStaticVar(tagVarName, loadTag);
481
481
 
482
482
  renderTagNode = this.generateRenderTagCode(codegen, tagVar, [inputProps, builder.identifierOut()]);
483
+ renderTagNode._isTagCall = true;
483
484
  } else {
484
485
  if (rendererRequirePath) {
485
486
  codegen.pushMeta("tags", rendererRequirePath, true);
@@ -516,8 +517,10 @@ class CustomTag extends HtmlElement {
516
517
 
517
518
  if (isNestedTag) {
518
519
  renderTagNode = builder.functionCall(tagVar, tagArgs);
520
+ renderTagNode._isNestedTagCall = true;
519
521
  } else {
520
522
  renderTagNode = this.generateRenderTagCode(codegen, tagVar, tagArgs);
523
+ renderTagNode._isTagCall = true;
521
524
  }
522
525
  }
523
526
 
@@ -533,10 +536,22 @@ class CustomTag extends HtmlElement {
533
536
  let renderBodyFunction;
534
537
 
535
538
  if (hasBody) {
536
- if (this._mergedRenderBodyStart !== undefined) {
537
- const mergedRenderBody = builder.renderBodyFunction(body.slice(this._mergedRenderBodyStart));
538
- body = body.slice(0, this._mergedRenderBodyStart);
539
- body.push(builder.returnStatement(mergedRenderBody));
539
+ if (this._hasDynamicNestedTags) {
540
+ const renderBody = [];
541
+ const newBody = [];
542
+
543
+ for (const child of body) {
544
+ if (hasHTML(child, context)) {
545
+ renderBody.push(child);
546
+ } else {
547
+ newBody.push(child);
548
+ }
549
+ }
550
+
551
+ if (renderBody.length) {
552
+ newBody.push(builder.returnStatement(builder.renderBodyFunction(renderBody)));
553
+ body = newBody;
554
+ }
540
555
  }
541
556
 
542
557
  if (tagDef.bodyFunction) {
@@ -625,27 +640,7 @@ class CustomTag extends HtmlElement {
625
640
  return null;
626
641
  } else {
627
642
  this._isDirectlyNestedTag = false;
628
-
629
- if (!parentCustomTag._hasDynamicNestedTags) {
630
- parentCustomTag._hasDynamicNestedTags = true;
631
-
632
- let nextBodyContent = parentCustomTag.firstChild;
633
- let i = 0;
634
-
635
- do {
636
- if (nextBodyContent.type === "Text" || nextBodyContent.type === "Scriptlet" || (nextBodyContent.type === "HtmlElement" || nextBodyContent.type === "CustomTag") && !(nextBodyContent.tagDef && (nextBodyContent.tagDef.isNestedTag || nextBodyContent.tagDef.codeGeneratorModulePath))) {
637
- parentCustomTag._mergedRenderBodyStart = i;
638
- break;
639
- }
640
-
641
- nextBodyContent = nextBodyContent.nextSibling;
642
- i++;
643
- } while (nextBodyContent);
644
-
645
- if (!i) {
646
- context.addError(parentCustomTag, "Render body content must come after any dynamic nested attribute tags.");
647
- }
648
- }
643
+ parentCustomTag._hasDynamicNestedTags = true;
649
644
  }
650
645
  }
651
646
 
@@ -660,4 +655,21 @@ class CustomTag extends HtmlElement {
660
655
  }
661
656
  }
662
657
 
658
+ function hasHTML(node, context) {
659
+ let hasHTML = false;
660
+ const walker = context.createWalker({
661
+ enter(node) {
662
+ if (node.type === "Html" || node.type === "TextVDOM" || node._isTagCall) {
663
+ hasHTML = true;
664
+ walker.stop();
665
+ } else if (node._isNestedTagCall) {
666
+ walker.skip();
667
+ }
668
+ }
669
+ });
670
+
671
+ walker.walk(node);
672
+ return hasHTML;
673
+ }
674
+
663
675
  module.exports = CustomTag;
@@ -148,7 +148,7 @@ function morphdom(fromNode, toNode, host, componentsContext) {
148
148
  if (curToNodeType === COMPONENT_NODE) {
149
149
  var component = curToNodeChild.v_;
150
150
  if ((matchingFromComponent = existingComponentLookup[component.id]) === undefined) {
151
- if (isHydrate === true) {
151
+ if (isHydrate) {
152
152
  var rootNode = beginFragmentNode(curFromNodeChild, fromNode);
153
153
  component._E_ = rootNode;
154
154
  componentByDOMNode.set(rootNode, component);
@@ -219,8 +219,12 @@ function morphdom(fromNode, toNode, host, componentsContext) {
219
219
  if (!curToNodeChild.ae_) {
220
220
  // We just skip over the fromNode if it is preserved
221
221
 
222
- if (compareNodeNames(curToNodeChild, curVFromNodeChild)) {
223
- morphEl(curFromNodeChild, curVFromNodeChild, curToNodeChild, parentComponent);
222
+ if (curVFromNodeChild && curToNodeType === curVFromNodeChild.bX_ && (curToNodeType !== ELEMENT_NODE || compareNodeNames(curToNodeChild, curVFromNodeChild))) {
223
+ if (curToNodeType === ELEMENT_NODE) {
224
+ morphEl(curFromNodeChild, curVFromNodeChild, curToNodeChild, parentComponent);
225
+ } else {
226
+ morphChildren(curFromNodeChild, curToNodeChild, parentComponent);
227
+ }
224
228
  } else {
225
229
  // Remove the old node
226
230
  detachNode(curFromNodeChild, fromNode, ownerComponent);
@@ -232,7 +236,7 @@ function morphdom(fromNode, toNode, host, componentsContext) {
232
236
  } else {
233
237
  matchingFromEl = referenceComponent.N_[curToNodeKey];
234
238
  if (matchingFromEl === undefined || matchingFromEl === curFromNodeChild) {
235
- if (isHydrate === true && curFromNodeChild) {
239
+ if (isHydrate && curFromNodeChild) {
236
240
  if (curFromNodeChild.nodeType === ELEMENT_NODE && (curToNodeChild.ae_ || caseInsensitiveCompare(curFromNodeChild.nodeName, curToNodeChild.cd_ || ""))) {
237
241
  curVFromNodeChild = virtualizeElement(curFromNodeChild);
238
242
  curVFromNodeChild.cd_ = curToNodeChild.cd_;
@@ -300,7 +304,7 @@ function morphdom(fromNode, toNode, host, componentsContext) {
300
304
  if (!curToNodeChild.ae_) {
301
305
  curVFromNodeChild = vElementByDOMNode.get(matchingFromEl);
302
306
 
303
- if (compareNodeNames(curVFromNodeChild, curToNodeChild)) {
307
+ if (curVFromNodeChild && curToNodeType === curVFromNodeChild.bX_ && (curToNodeType !== ELEMENT_NODE || compareNodeNames(curVFromNodeChild, curToNodeChild))) {
304
308
  if (fromNextSibling === matchingFromEl) {
305
309
  // Single element removal:
306
310
  // A <-> A
@@ -348,7 +352,11 @@ function morphdom(fromNode, toNode, host, componentsContext) {
348
352
  }
349
353
  }
350
354
 
351
- morphEl(matchingFromEl, curVFromNodeChild, curToNodeChild, parentComponent);
355
+ if (curToNodeType === ELEMENT_NODE) {
356
+ morphEl(matchingFromEl, curVFromNodeChild, curToNodeChild, parentComponent);
357
+ } else {
358
+ morphChildren(matchingFromEl, curToNodeChild, parentComponent);
359
+ }
352
360
  } else {
353
361
  insertVirtualNodeBefore(curToNodeChild, curToNodeKey, curFromNodeChild, fromNode, ownerComponent, parentComponent);
354
362
  detachNode(matchingFromEl, fromNode, ownerComponent);
@@ -398,7 +406,7 @@ function morphdom(fromNode, toNode, host, componentsContext) {
398
406
  // Both nodes being compared are Element nodes
399
407
  curVFromNodeChild = vElementByDOMNode.get(curFromNodeChild);
400
408
  if (curVFromNodeChild === undefined) {
401
- if (isHydrate === true) {
409
+ if (isHydrate) {
402
410
  curVFromNodeChild = virtualizeElement(curFromNodeChild);
403
411
 
404
412
  if (caseInsensitiveCompare(curVFromNodeChild.cd_, curToNodeChild.cd_)) {
@@ -426,14 +434,18 @@ function morphdom(fromNode, toNode, host, componentsContext) {
426
434
  } else if (curFromNodeType === TEXT_NODE || curFromNodeType === COMMENT_NODE) {
427
435
  // Both nodes being compared are Text or Comment nodes
428
436
  isCompatible = true;
429
- // Simply update nodeValue on the original node to
430
- // change the text value
431
-
432
- if (isHydrate === true && toNextSibling && curFromNodeType === TEXT_NODE && toNextSibling.bX_ === TEXT_NODE) {
433
- fromNextSibling = curFromNodeChild.splitText(curToNodeChild.bW_.length);
434
- }
435
- if (curFromNodeChild.nodeValue !== curToNodeChild.bW_) {
436
- curFromNodeChild.nodeValue = curToNodeChild.bW_;
437
+ var curToNodeValue = curToNodeChild.bW_;
438
+ var curFromNodeValue = curFromNodeChild.nodeValue;
439
+ if (curFromNodeValue !== curToNodeValue) {
440
+ if (isHydrate && curFromNodeType === TEXT_NODE && curFromNodeValue.startsWith(curToNodeValue)) {
441
+ // In hydrate mode we can use splitText to more efficiently handle
442
+ // adjacent text vdom nodes that were merged.
443
+ fromNextSibling = curFromNodeChild.splitText(curToNodeValue.length);
444
+ } else {
445
+ // Simply update nodeValue on the original node to
446
+ // change the text value
447
+ curFromNodeChild.nodeValue = curToNodeValue;
448
+ }
437
449
  }
438
450
  }
439
451
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "marko",
3
- "version": "4.28.0",
3
+ "version": "4.28.2",
4
4
  "license": "MIT",
5
5
  "description": "UI Components + streaming, async, high performance, HTML templating for Node.js and the browser.",
6
6
  "dependencies": {
@@ -560,6 +560,7 @@ class CustomTag extends HtmlElement {
560
560
  inputProps,
561
561
  builder.identifierOut(),
562
562
  ]);
563
+ renderTagNode._isTagCall = true;
563
564
  } else {
564
565
  if (rendererRequirePath) {
565
566
  codegen.pushMeta("tags", rendererRequirePath, true);
@@ -623,8 +624,10 @@ class CustomTag extends HtmlElement {
623
624
 
624
625
  if (isNestedTag) {
625
626
  renderTagNode = builder.functionCall(tagVar, tagArgs);
627
+ renderTagNode._isNestedTagCall = true
626
628
  } else {
627
629
  renderTagNode = this.generateRenderTagCode(codegen, tagVar, tagArgs);
630
+ renderTagNode._isTagCall = true;
628
631
  }
629
632
  }
630
633
 
@@ -640,12 +643,22 @@ class CustomTag extends HtmlElement {
640
643
  let renderBodyFunction;
641
644
 
642
645
  if (hasBody) {
643
- if (this._mergedRenderBodyStart !== undefined) {
644
- const mergedRenderBody = builder.renderBodyFunction(
645
- body.slice(this._mergedRenderBodyStart)
646
- );
647
- body = body.slice(0, this._mergedRenderBodyStart);
648
- body.push(builder.returnStatement(mergedRenderBody));
646
+ if (this._hasDynamicNestedTags) {
647
+ const renderBody = [];
648
+ const newBody = [];
649
+
650
+ for (const child of body) {
651
+ if (hasHTML(child, context)) {
652
+ renderBody.push(child);
653
+ } else {
654
+ newBody.push(child);
655
+ }
656
+ }
657
+
658
+ if (renderBody.length) {
659
+ newBody.push(builder.returnStatement(builder.renderBodyFunction(renderBody)));
660
+ body = newBody;
661
+ }
649
662
  }
650
663
 
651
664
  if (tagDef.bodyFunction) {
@@ -756,40 +769,7 @@ class CustomTag extends HtmlElement {
756
769
  return null;
757
770
  } else {
758
771
  this._isDirectlyNestedTag = false;
759
-
760
- if (!parentCustomTag._hasDynamicNestedTags) {
761
- parentCustomTag._hasDynamicNestedTags = true;
762
-
763
- let nextBodyContent = parentCustomTag.firstChild;
764
- let i = 0;
765
-
766
- do {
767
- if (
768
- nextBodyContent.type === "Text" ||
769
- nextBodyContent.type === "Scriptlet" ||
770
- ((nextBodyContent.type === "HtmlElement" ||
771
- nextBodyContent.type === "CustomTag") &&
772
- !(
773
- nextBodyContent.tagDef &&
774
- (nextBodyContent.tagDef.isNestedTag ||
775
- nextBodyContent.tagDef.codeGeneratorModulePath)
776
- ))
777
- ) {
778
- parentCustomTag._mergedRenderBodyStart = i;
779
- break;
780
- }
781
-
782
- nextBodyContent = nextBodyContent.nextSibling;
783
- i++;
784
- } while (nextBodyContent);
785
-
786
- if (!i) {
787
- context.addError(
788
- parentCustomTag,
789
- "Render body content must come after any dynamic nested attribute tags."
790
- );
791
- }
792
- }
772
+ parentCustomTag._hasDynamicNestedTags = true;
793
773
  }
794
774
  }
795
775
 
@@ -812,4 +792,21 @@ class CustomTag extends HtmlElement {
812
792
  }
813
793
  }
814
794
 
795
+ function hasHTML(node, context) {
796
+ let hasHTML = false;
797
+ const walker = context.createWalker({
798
+ enter(node) {
799
+ if (node.type === "Html" || node.type === "TextVDOM" || node._isTagCall) {
800
+ hasHTML = true;
801
+ walker.stop();
802
+ } else if (node._isNestedTagCall) {
803
+ walker.skip();
804
+ }
805
+ },
806
+ });
807
+
808
+ walker.walk(node);
809
+ return hasHTML;
810
+ }
811
+
815
812
  module.exports = CustomTag;
@@ -180,7 +180,7 @@ function morphdom(fromNode, toNode, host, componentsContext) {
180
180
  (matchingFromComponent = existingComponentLookup[component.id]) ===
181
181
  undefined
182
182
  ) {
183
- if (isHydrate === true) {
183
+ if (isHydrate) {
184
184
  var rootNode = beginFragmentNode(curFromNodeChild, fromNode);
185
185
  component.___rootNode = rootNode;
186
186
  componentByDOMNode.set(rootNode, component);
@@ -281,13 +281,26 @@ function morphdom(fromNode, toNode, host, componentsContext) {
281
281
  if (!curToNodeChild.___preserve) {
282
282
  // We just skip over the fromNode if it is preserved
283
283
 
284
- if (compareNodeNames(curToNodeChild, curVFromNodeChild)) {
285
- morphEl(
286
- curFromNodeChild,
287
- curVFromNodeChild,
288
- curToNodeChild,
289
- parentComponent
290
- );
284
+ if (
285
+ curVFromNodeChild &&
286
+ curToNodeType === curVFromNodeChild.___nodeType &&
287
+ (curToNodeType !== ELEMENT_NODE ||
288
+ compareNodeNames(curToNodeChild, curVFromNodeChild))
289
+ ) {
290
+ if (curToNodeType === ELEMENT_NODE) {
291
+ morphEl(
292
+ curFromNodeChild,
293
+ curVFromNodeChild,
294
+ curToNodeChild,
295
+ parentComponent,
296
+ );
297
+ } else {
298
+ morphChildren(
299
+ curFromNodeChild,
300
+ curToNodeChild,
301
+ parentComponent,
302
+ );
303
+ }
291
304
  } else {
292
305
  // Remove the old node
293
306
  detachNode(curFromNodeChild, fromNode, ownerComponent);
@@ -309,7 +322,7 @@ function morphdom(fromNode, toNode, host, componentsContext) {
309
322
  matchingFromEl === undefined ||
310
323
  matchingFromEl === curFromNodeChild
311
324
  ) {
312
- if (isHydrate === true && curFromNodeChild) {
325
+ if (isHydrate && curFromNodeChild) {
313
326
  if (
314
327
  curFromNodeChild.nodeType === ELEMENT_NODE &&
315
328
  (curToNodeChild.___preserve ||
@@ -404,7 +417,12 @@ function morphdom(fromNode, toNode, host, componentsContext) {
404
417
  if (!curToNodeChild.___preserve) {
405
418
  curVFromNodeChild = vElementByDOMNode.get(matchingFromEl);
406
419
 
407
- if (compareNodeNames(curVFromNodeChild, curToNodeChild)) {
420
+ if (
421
+ curVFromNodeChild &&
422
+ curToNodeType === curVFromNodeChild.___nodeType &&
423
+ (curToNodeType !== ELEMENT_NODE ||
424
+ compareNodeNames(curVFromNodeChild, curToNodeChild))
425
+ ) {
408
426
  if (fromNextSibling === matchingFromEl) {
409
427
  // Single element removal:
410
428
  // A <-> A
@@ -455,12 +473,21 @@ function morphdom(fromNode, toNode, host, componentsContext) {
455
473
  }
456
474
  }
457
475
 
458
- morphEl(
459
- matchingFromEl,
460
- curVFromNodeChild,
461
- curToNodeChild,
462
- parentComponent
463
- );
476
+ if (curToNodeType === ELEMENT_NODE) {
477
+ morphEl(
478
+ matchingFromEl,
479
+ curVFromNodeChild,
480
+ curToNodeChild,
481
+ parentComponent
482
+ );
483
+ } else {
484
+ morphChildren(
485
+ matchingFromEl,
486
+ curToNodeChild,
487
+ parentComponent,
488
+ );
489
+ }
490
+
464
491
  } else {
465
492
  insertVirtualNodeBefore(
466
493
  curToNodeChild,
@@ -519,7 +546,7 @@ function morphdom(fromNode, toNode, host, componentsContext) {
519
546
  // Both nodes being compared are Element nodes
520
547
  curVFromNodeChild = vElementByDOMNode.get(curFromNodeChild);
521
548
  if (curVFromNodeChild === undefined) {
522
- if (isHydrate === true) {
549
+ if (isHydrate) {
523
550
  curVFromNodeChild = virtualizeElement(curFromNodeChild);
524
551
 
525
552
  if (
@@ -562,21 +589,24 @@ function morphdom(fromNode, toNode, host, componentsContext) {
562
589
  ) {
563
590
  // Both nodes being compared are Text or Comment nodes
564
591
  isCompatible = true;
565
- // Simply update nodeValue on the original node to
566
- // change the text value
567
-
568
- if (
569
- isHydrate === true &&
570
- toNextSibling &&
571
- curFromNodeType === TEXT_NODE &&
572
- toNextSibling.___nodeType === TEXT_NODE
573
- ) {
574
- fromNextSibling = curFromNodeChild.splitText(
575
- curToNodeChild.___nodeValue.length
576
- );
577
- }
578
- if (curFromNodeChild.nodeValue !== curToNodeChild.___nodeValue) {
579
- curFromNodeChild.nodeValue = curToNodeChild.___nodeValue;
592
+ var curToNodeValue = curToNodeChild.___nodeValue;
593
+ var curFromNodeValue = curFromNodeChild.nodeValue;
594
+ if (curFromNodeValue !== curToNodeValue) {
595
+ if (
596
+ isHydrate &&
597
+ curFromNodeType === TEXT_NODE &&
598
+ curFromNodeValue.startsWith(curToNodeValue)
599
+ ) {
600
+ // In hydrate mode we can use splitText to more efficiently handle
601
+ // adjacent text vdom nodes that were merged.
602
+ fromNextSibling = curFromNodeChild.splitText(
603
+ curToNodeValue.length,
604
+ );
605
+ } else {
606
+ // Simply update nodeValue on the original node to
607
+ // change the text value
608
+ curFromNodeChild.nodeValue = curToNodeValue;
609
+ }
580
610
  }
581
611
  }
582
612
  }