@angular/platform-server 21.2.20 → 21.2.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/platform-server",
3
- "version": "21.2.20",
3
+ "version": "21.2.22",
4
4
  "description": "Angular - library for using Angular in Node.js",
5
5
  "author": "angular",
6
6
  "license": "MIT",
@@ -8,10 +8,10 @@
8
8
  "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
9
9
  },
10
10
  "peerDependencies": {
11
- "@angular/common": "21.2.20",
12
- "@angular/compiler": "21.2.20",
13
- "@angular/core": "21.2.20",
14
- "@angular/platform-browser": "21.2.20",
11
+ "@angular/common": "21.2.22",
12
+ "@angular/compiler": "21.2.22",
13
+ "@angular/core": "21.2.22",
14
+ "@angular/platform-browser": "21.2.22",
15
15
  "rxjs": "^6.5.3 || ^7.4.0"
16
16
  },
17
17
  "dependencies": {
@@ -848,6 +848,23 @@ function requireNodeUtils () {
848
848
  return a.name;
849
849
  }
850
850
 
851
+ function fallbackRawContentTags(node) {
852
+ const tags = [];
853
+ while (node) {
854
+ if (node.nodeType === 1 /*ELEMENT_NODE*/) {
855
+ if (node.namespaceURI === NAMESPACE.HTML && hasRawContentFallback[node.tagName]) {
856
+ tags.push(node.localName);
857
+ }
858
+ node = node.parentNode;
859
+ } else if (node.nodeType === 11 /*DOCUMENT_FRAGMENT_NODE*/ && node._host) {
860
+ node = node._host;
861
+ } else {
862
+ node = node.parentNode;
863
+ }
864
+ }
865
+ return tags;
866
+ }
867
+
851
868
  /**
852
869
  * Escapes matching closing tag in a raw text.
853
870
  *
@@ -877,12 +894,22 @@ function requireNodeUtils () {
877
894
  return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
878
895
  }
879
896
 
880
- function escapeFallbackRawText(rawText, parentTag) {
881
- var result = '';
882
- var index = 0;
897
+ function escapeMatchingClosingTags(rawText, parentTag, ancestorTags) {
898
+ let result = escapeMatchingClosingTag(rawText, parentTag);
899
+ if (ancestorTags) {
900
+ for (const ancestorTag of ancestorTags) {
901
+ result = escapeMatchingClosingTag(result, ancestorTag);
902
+ }
903
+ }
904
+ return result;
905
+ }
906
+
907
+ function escapeFallbackRawText(rawText, parentTag, ancestorTags) {
908
+ let result = '';
909
+ let index = 0;
883
910
 
884
911
  while (index < rawText.length) {
885
- var commentStart = rawText.indexOf('<!--', index);
912
+ const commentStart = rawText.indexOf('<!--', index);
886
913
  if (commentStart === -1) {
887
914
  result += escape(rawText.slice(index));
888
915
  break;
@@ -890,15 +917,15 @@ function requireNodeUtils () {
890
917
 
891
918
  result += escape(rawText.slice(index, commentStart));
892
919
 
893
- var commentEnd = findCommentEnd(rawText, commentStart + 4);
920
+ const commentEnd = findCommentEnd(rawText, commentStart + 4);
894
921
  if (commentEnd === -1) {
895
- result += escapeMatchingClosingTag(rawText.slice(commentStart), parentTag);
922
+ result += escapeMatchingClosingTags(rawText.slice(commentStart), parentTag, ancestorTags);
896
923
  break;
897
924
  }
898
925
 
899
926
  // A complete HTML comment remains inert if downstream tooling reparses
900
927
  // fallback raw text as normal HTML, so preserve its comment semantics.
901
- result += escapeMatchingClosingTag(rawText.slice(commentStart, commentEnd), parentTag);
928
+ result += escapeMatchingClosingTags(rawText.slice(commentStart, commentEnd), parentTag, ancestorTags);
902
929
  index = commentEnd;
903
930
  }
904
931
 
@@ -911,7 +938,7 @@ function requireNodeUtils () {
911
938
  if (rawText.charAt(index) === '-' && rawText.charAt(index + 1) === '>')
912
939
  return index + 2;
913
940
 
914
- var match = CLOSING_COMMENT_REGEXP.exec(rawText.slice(index));
941
+ const match = CLOSING_COMMENT_REGEXP.exec(rawText.slice(index));
915
942
  return match ? index + match.index + match[0].length : -1;
916
943
  }
917
944
 
@@ -962,8 +989,12 @@ function requireNodeUtils () {
962
989
  // If an element can have raw content, this content may
963
990
  // potentially require escaping to avoid XSS.
964
991
  var upperTag = tagname.toUpperCase();
965
- if (hasRawContent[upperTag] && !hasRawContentFallback[upperTag]) {
992
+ if (hasRawContent[upperTag] && !hasRawContentFallback[upperTag] && ss.includes('</')) {
966
993
  ss = escapeMatchingClosingTag(ss, tagname);
994
+ const fallbackTags = fallbackRawContentTags(parent);
995
+ for (const fallbackTag of fallbackTags) {
996
+ ss = escapeMatchingClosingTag(ss, fallbackTag);
997
+ }
967
998
  }
968
999
  if (html && extraNewLine[tagname] && ss.charAt(0)==='\n') s += '\n';
969
1000
  // Serialize children and add end tag for all others
@@ -983,22 +1014,33 @@ function requireNodeUtils () {
983
1014
  if (hasRawContent[parenttag]) {
984
1015
  // Preserve actual child element markup in fallback elements such as
985
1016
  // <noscript>, but do not emit text-node payloads as raw HTML.
986
- s += hasRawContentFallback[parenttag] ? escapeFallbackRawText(kid.data, parent.localName) : kid.data;
1017
+ s += hasRawContentFallback[parenttag] ? escapeFallbackRawText(kid.data, parent.localName, fallbackRawContentTags(parent.parentNode)) : kid.data;
987
1018
  } else {
988
1019
  s += escape(kid.data);
989
1020
  }
990
1021
  break;
991
1022
  case 8: //COMMENT_NODE
992
- s += '<!--' + escapeClosingCommentTag(kid.data) + '-->';
1023
+ let commentData = escapeClosingCommentTag(kid.data);
1024
+ if (commentData.includes('</')) {
1025
+ const fallbackTags = fallbackRawContentTags(parent);
1026
+ for (const fallbackTag of fallbackTags) {
1027
+ commentData = escapeMatchingClosingTag(commentData, fallbackTag);
1028
+ }
1029
+ }
1030
+ s += '<!--' + commentData + '-->';
993
1031
  break;
994
1032
  case 7: //PROCESSING_INSTRUCTION_NODE
995
- const content = escapeProcessingInstructionContent(kid.data);
1033
+ let content = escapeProcessingInstructionContent(kid.data);
1034
+ if (content.includes('</')) {
1035
+ const fallbackTags = fallbackRawContentTags(parent);
1036
+ for (const fallbackTag of fallbackTags) {
1037
+ content = escapeMatchingClosingTag(content, fallbackTag);
1038
+ }
1039
+ }
996
1040
  s += '<?' + kid.target + ' ' + content + '?>';
997
1041
  break;
998
1042
  case 10: //DOCUMENT_TYPE_NODE
999
- s += '<!DOCTYPE ' + kid.name;
1000
-
1001
- s += '>';
1043
+ s += '<!DOCTYPE ' + kid.name + '>';
1002
1044
  break;
1003
1045
  default:
1004
1046
  utils.InvalidStateError();
@@ -7588,7 +7630,7 @@ function requireHtmlelts () {
7588
7630
  required: Boolean,
7589
7631
  readOnly: Boolean,
7590
7632
  checked: Boolean,
7591
- value: String,
7633
+ value: { type: String, treatNullAsEmptyString: true },
7592
7634
  src: URL,
7593
7635
  defaultChecked: {name: 'checked', type: Boolean},
7594
7636
  size: {type: 'unsigned long', default: 20, min: 1, setmin: 1},
@@ -8111,10 +8153,21 @@ function requireHtmlelts () {
8111
8153
  ctor: function HTMLTemplateElement(doc, localName, prefix) {
8112
8154
  HTMLElement.call(this, doc, localName, prefix);
8113
8155
  this._contentFragment = doc._templateDoc.createDocumentFragment();
8156
+ this._contentFragment._host = this;
8114
8157
  },
8115
8158
  props: {
8116
8159
  content: { get: function() { return this._contentFragment; } },
8117
- serialize: { value: function() { return this.content.serialize(); } }
8160
+ serialize: { value: function() { return this.content.serialize(); } },
8161
+ cloneNode: {
8162
+ value: function(deep) {
8163
+ var clone = HTMLElement.prototype.cloneNode.call(this, deep);
8164
+ if (deep) {
8165
+ clone._contentFragment = this._contentFragment.cloneNode(true);
8166
+ clone._contentFragment._host = clone;
8167
+ }
8168
+ return clone;
8169
+ }
8170
+ }
8118
8171
  }
8119
8172
  });
8120
8173