@angular/platform-server 22.1.0-next.5 → 22.1.0-rc.0

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.
@@ -749,6 +749,15 @@ function requireNodeUtils () {
749
749
  PLAINTEXT: true
750
750
  };
751
751
 
752
+ var hasRawContentFallback = {
753
+ // Text in these fallback raw-content elements is inert for browser parsing,
754
+ // but downstream SSR post-processing may reparse it without raw-text state.
755
+ IFRAME: true,
756
+ NOEMBED: true,
757
+ NOSCRIPT: true,
758
+ NOFRAMES: true
759
+ };
760
+
752
761
  var emptyElements = {
753
762
  area: true,
754
763
  base: true,
@@ -849,7 +858,7 @@ function requireNodeUtils () {
849
858
  * and unsafe behavior after de-serialization.
850
859
  */
851
860
  function escapeMatchingClosingTag(rawText, parentTag) {
852
- const parentClosingTag = '</' + parentTag;
861
+ const parentClosingTag = ('</' + parentTag).toLowerCase();
853
862
  if (!rawText.toLowerCase().includes(parentClosingTag)) {
854
863
  return rawText; // fast path
855
864
  }
@@ -859,11 +868,53 @@ function requireNodeUtils () {
859
868
  // would otherwise shift the replacement and leave a real `</tag>`
860
869
  // break-out in the output.
861
870
  return rawText.replace(
862
- new RegExp(parentClosingTag, 'ig'),
871
+ new RegExp(escapeRegExp(parentClosingTag), 'ig'),
863
872
  (m) => '&lt;' + m.slice(1)
864
873
  );
865
874
  }
866
875
 
876
+ function escapeRegExp(text) {
877
+ return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
878
+ }
879
+
880
+ function escapeFallbackRawText(rawText, parentTag) {
881
+ var result = '';
882
+ var index = 0;
883
+
884
+ while (index < rawText.length) {
885
+ var commentStart = rawText.indexOf('<!--', index);
886
+ if (commentStart === -1) {
887
+ result += escape(rawText.slice(index));
888
+ break;
889
+ }
890
+
891
+ result += escape(rawText.slice(index, commentStart));
892
+
893
+ var commentEnd = findCommentEnd(rawText, commentStart + 4);
894
+ if (commentEnd === -1) {
895
+ result += escapeMatchingClosingTag(rawText.slice(commentStart), parentTag);
896
+ break;
897
+ }
898
+
899
+ // A complete HTML comment remains inert if downstream tooling reparses
900
+ // fallback raw text as normal HTML, so preserve its comment semantics.
901
+ result += escapeMatchingClosingTag(rawText.slice(commentStart, commentEnd), parentTag);
902
+ index = commentEnd;
903
+ }
904
+
905
+ return result;
906
+ }
907
+
908
+ function findCommentEnd(rawText, index) {
909
+ if (rawText.charAt(index) === '>')
910
+ return index + 1;
911
+ if (rawText.charAt(index) === '-' && rawText.charAt(index + 1) === '>')
912
+ return index + 2;
913
+
914
+ var match = CLOSING_COMMENT_REGEXP.exec(rawText.slice(index));
915
+ return match ? index + match.index + match[0].length : -1;
916
+ }
917
+
867
918
  const CLOSING_COMMENT_REGEXP = /--!?>/;
868
919
 
869
920
  /**
@@ -911,7 +962,7 @@ function requireNodeUtils () {
911
962
  // If an element can have raw content, this content may
912
963
  // potentially require escaping to avoid XSS.
913
964
  var upperTag = tagname.toUpperCase();
914
- if (hasRawContent[upperTag]) {
965
+ if (hasRawContent[upperTag] && !hasRawContentFallback[upperTag]) {
915
966
  ss = escapeMatchingClosingTag(ss, tagname);
916
967
  }
917
968
  if (html && extraNewLine[tagname] && ss.charAt(0)==='\n') s += '\n';
@@ -930,7 +981,9 @@ function requireNodeUtils () {
930
981
  parenttag = '';
931
982
 
932
983
  if (hasRawContent[parenttag]) {
933
- s += kid.data;
984
+ // Preserve actual child element markup in fallback elements such as
985
+ // <noscript>, but do not emit text-node payloads as raw HTML.
986
+ s += hasRawContentFallback[parenttag] ? escapeFallbackRawText(kid.data, parent.localName) : kid.data;
934
987
  } else {
935
988
  s += escape(kid.data);
936
989
  }