@angular/platform-server 20.3.29 → 20.3.31

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/fesm2022/init.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v20.3.29
2
+ * @license Angular v20.3.31
3
3
  * (c) 2010-2025 Google LLC. https://angular.dev/
4
4
  * License: MIT
5
5
  */
@@ -854,6 +854,23 @@ function requireNodeUtils () {
854
854
  return a.name;
855
855
  }
856
856
 
857
+ function fallbackRawContentTags(node) {
858
+ const tags = [];
859
+ while (node) {
860
+ if (node.nodeType === 1 /*ELEMENT_NODE*/) {
861
+ if (node.namespaceURI === NAMESPACE.HTML && hasRawContentFallback[node.tagName]) {
862
+ tags.push(node.localName);
863
+ }
864
+ node = node.parentNode;
865
+ } else if (node.nodeType === 11 /*DOCUMENT_FRAGMENT_NODE*/ && node._host) {
866
+ node = node._host;
867
+ } else {
868
+ node = node.parentNode;
869
+ }
870
+ }
871
+ return tags;
872
+ }
873
+
857
874
  /**
858
875
  * Escapes matching closing tag in a raw text.
859
876
  *
@@ -883,12 +900,22 @@ function requireNodeUtils () {
883
900
  return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
884
901
  }
885
902
 
886
- function escapeFallbackRawText(rawText, parentTag) {
887
- var result = '';
888
- var index = 0;
903
+ function escapeMatchingClosingTags(rawText, parentTag, ancestorTags) {
904
+ let result = escapeMatchingClosingTag(rawText, parentTag);
905
+ if (ancestorTags) {
906
+ for (const ancestorTag of ancestorTags) {
907
+ result = escapeMatchingClosingTag(result, ancestorTag);
908
+ }
909
+ }
910
+ return result;
911
+ }
912
+
913
+ function escapeFallbackRawText(rawText, parentTag, ancestorTags) {
914
+ let result = '';
915
+ let index = 0;
889
916
 
890
917
  while (index < rawText.length) {
891
- var commentStart = rawText.indexOf('<!--', index);
918
+ const commentStart = rawText.indexOf('<!--', index);
892
919
  if (commentStart === -1) {
893
920
  result += escape(rawText.slice(index));
894
921
  break;
@@ -896,15 +923,15 @@ function requireNodeUtils () {
896
923
 
897
924
  result += escape(rawText.slice(index, commentStart));
898
925
 
899
- var commentEnd = findCommentEnd(rawText, commentStart + 4);
926
+ const commentEnd = findCommentEnd(rawText, commentStart + 4);
900
927
  if (commentEnd === -1) {
901
- result += escapeMatchingClosingTag(rawText.slice(commentStart), parentTag);
928
+ result += escapeMatchingClosingTags(rawText.slice(commentStart), parentTag, ancestorTags);
902
929
  break;
903
930
  }
904
931
 
905
932
  // A complete HTML comment remains inert if downstream tooling reparses
906
933
  // fallback raw text as normal HTML, so preserve its comment semantics.
907
- result += escapeMatchingClosingTag(rawText.slice(commentStart, commentEnd), parentTag);
934
+ result += escapeMatchingClosingTags(rawText.slice(commentStart, commentEnd), parentTag, ancestorTags);
908
935
  index = commentEnd;
909
936
  }
910
937
 
@@ -917,24 +944,47 @@ function requireNodeUtils () {
917
944
  if (rawText.charAt(index) === '-' && rawText.charAt(index + 1) === '>')
918
945
  return index + 2;
919
946
 
920
- var match = CLOSING_COMMENT_REGEXP.exec(rawText.slice(index));
947
+ const match = CLOSING_COMMENT_REGEXP.exec(rawText.slice(index));
921
948
  return match ? index + match.index + match[0].length : -1;
922
949
  }
923
950
 
924
951
  const CLOSING_COMMENT_REGEXP = /--!?>/;
925
952
 
953
+ /**
954
+ * Escapes a comment content that abruptly closes the comment.
955
+ *
956
+ * A comment can not carry content that starts with `>` or `->`: the parser
957
+ * closes the comment as soon as it comes across such a sequence right after
958
+ * `<!--` (the "abrupt-closing-of-empty-comment" parse error). For example,
959
+ * `#comment('><img src=x onerror=alert(1)>')` would otherwise serialize into
960
+ * `<!--><img src=x onerror=alert(1)>-->`, which de-serializes into an empty
961
+ * comment followed by a live `<img>` element. Escaping the leading `>` keeps
962
+ * the content inside the comment, where it stays inert.
963
+ */
964
+ function escapeAbruptClosingCommentTag(rawContent) {
965
+ if (rawContent.startsWith('>')) {
966
+ return '&gt;' + rawContent.slice(1);
967
+ }
968
+ if (rawContent.startsWith('->')) {
969
+ return '-&gt;' + rawContent.slice(2);
970
+ }
971
+ return rawContent; // fast path
972
+ }
973
+
926
974
  /**
927
975
  * Escapes closing comment tag in a comment content.
928
976
  *
929
977
  * For example, given `#comment('-->')`, the content of a comment would be
930
978
  * updated to `--&gt;` to avoid unexpected and unsafe behavior after
931
- * de-serialization.
979
+ * de-serialization. Content that abruptly closes an empty comment is
980
+ * escaped as well, see `escapeAbruptClosingCommentTag()`.
932
981
  */
933
982
  function escapeClosingCommentTag(rawContent) {
934
- if (!CLOSING_COMMENT_REGEXP.test(rawContent)) {
935
- return rawContent; // fast path
983
+ const content = escapeAbruptClosingCommentTag(rawContent);
984
+ if (!CLOSING_COMMENT_REGEXP.test(content)) {
985
+ return content; // fast path
936
986
  }
937
- return rawContent.replace(/(--\!?)>/g, '$1&gt;');
987
+ return content.replace(/(--\!?)>/g, '$1&gt;');
938
988
  }
939
989
 
940
990
  /**
@@ -968,8 +1018,12 @@ function requireNodeUtils () {
968
1018
  // If an element can have raw content, this content may
969
1019
  // potentially require escaping to avoid XSS.
970
1020
  var upperTag = tagname.toUpperCase();
971
- if (hasRawContent[upperTag] && !hasRawContentFallback[upperTag]) {
1021
+ if (hasRawContent[upperTag] && !hasRawContentFallback[upperTag] && ss.includes('</')) {
972
1022
  ss = escapeMatchingClosingTag(ss, tagname);
1023
+ const fallbackTags = fallbackRawContentTags(parent);
1024
+ for (const fallbackTag of fallbackTags) {
1025
+ ss = escapeMatchingClosingTag(ss, fallbackTag);
1026
+ }
973
1027
  }
974
1028
  if (html && extraNewLine[tagname] && ss.charAt(0)==='\n') s += '\n';
975
1029
  // Serialize children and add end tag for all others
@@ -989,22 +1043,33 @@ function requireNodeUtils () {
989
1043
  if (hasRawContent[parenttag]) {
990
1044
  // Preserve actual child element markup in fallback elements such as
991
1045
  // <noscript>, but do not emit text-node payloads as raw HTML.
992
- s += hasRawContentFallback[parenttag] ? escapeFallbackRawText(kid.data, parent.localName) : kid.data;
1046
+ s += hasRawContentFallback[parenttag] ? escapeFallbackRawText(kid.data, parent.localName, fallbackRawContentTags(parent.parentNode)) : kid.data;
993
1047
  } else {
994
1048
  s += escape(kid.data);
995
1049
  }
996
1050
  break;
997
1051
  case 8: //COMMENT_NODE
998
- s += '<!--' + escapeClosingCommentTag(kid.data) + '-->';
1052
+ let commentData = escapeClosingCommentTag(kid.data);
1053
+ if (commentData.includes('</')) {
1054
+ const fallbackTags = fallbackRawContentTags(parent);
1055
+ for (const fallbackTag of fallbackTags) {
1056
+ commentData = escapeMatchingClosingTag(commentData, fallbackTag);
1057
+ }
1058
+ }
1059
+ s += '<!--' + commentData + '-->';
999
1060
  break;
1000
1061
  case 7: //PROCESSING_INSTRUCTION_NODE
1001
- const content = escapeProcessingInstructionContent(kid.data);
1062
+ let content = escapeProcessingInstructionContent(kid.data);
1063
+ if (content.includes('</')) {
1064
+ const fallbackTags = fallbackRawContentTags(parent);
1065
+ for (const fallbackTag of fallbackTags) {
1066
+ content = escapeMatchingClosingTag(content, fallbackTag);
1067
+ }
1068
+ }
1002
1069
  s += '<?' + kid.target + ' ' + content + '?>';
1003
1070
  break;
1004
1071
  case 10: //DOCUMENT_TYPE_NODE
1005
- s += '<!DOCTYPE ' + kid.name;
1006
-
1007
- s += '>';
1072
+ s += '<!DOCTYPE ' + kid.name + '>';
1008
1073
  break;
1009
1074
  default:
1010
1075
  utils.InvalidStateError();
@@ -7588,7 +7653,7 @@ function requireHtmlelts () {
7588
7653
  required: Boolean,
7589
7654
  readOnly: Boolean,
7590
7655
  checked: Boolean,
7591
- value: String,
7656
+ value: { type: String, treatNullAsEmptyString: true },
7592
7657
  src: URL,
7593
7658
  defaultChecked: {name: 'checked', type: Boolean},
7594
7659
  size: {type: 'unsigned long', default: 20, min: 1, setmin: 1},
@@ -8111,10 +8176,21 @@ function requireHtmlelts () {
8111
8176
  ctor: function HTMLTemplateElement(doc, localName, prefix) {
8112
8177
  HTMLElement.call(this, doc, localName, prefix);
8113
8178
  this._contentFragment = doc._templateDoc.createDocumentFragment();
8179
+ this._contentFragment._host = this;
8114
8180
  },
8115
8181
  props: {
8116
8182
  content: { get: function() { return this._contentFragment; } },
8117
- serialize: { value: function() { return this.content.serialize(); } }
8183
+ serialize: { value: function() { return this.content.serialize(); } },
8184
+ cloneNode: {
8185
+ value: function(deep) {
8186
+ var clone = HTMLElement.prototype.cloneNode.call(this, deep);
8187
+ if (deep) {
8188
+ clone._contentFragment = this._contentFragment.cloneNode(true);
8189
+ clone._contentFragment._host = clone;
8190
+ }
8191
+ return clone;
8192
+ }
8193
+ }
8118
8194
  }
8119
8195
  });
8120
8196
 
@@ -14180,6 +14256,8 @@ function requireHTMLParser () {
14180
14256
  emitDoctype();
14181
14257
  break;
14182
14258
  case -1: // EOF
14259
+ // Lookahead states must consume the EOF marker explicitly.
14260
+ nextchar += 1;
14183
14261
  forcequirks();
14184
14262
  emitDoctype();
14185
14263
  emitEOF();