@webjsdev/server 0.8.18 → 0.8.19

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/ssr.js +25 -13
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webjsdev/server",
3
- "version": "0.8.18",
3
+ "version": "0.8.19",
4
4
  "type": "module",
5
5
  "description": "webjs dev/prod server: SSR, router, API, server actions, live reload",
6
6
  "main": "index.js",
package/src/ssr.js CHANGED
@@ -586,16 +586,9 @@ async function collectMetadata(route, ctx, dev) {
586
586
  * @returns {{ head: string, body: string }}
587
587
  */
588
588
  function hoistHeadTags(headHtml, bodyHtml) {
589
- const hoisted = [];
590
- // <script>…</script> and <style>…</style> are paired; <link …> is void.
591
- const re = /^\s*(<script[\s>][\s\S]*?<\/script>|<style[\s>][\s\S]*?<\/style>|<link\b[^>]*>)/i;
592
-
593
- let remaining = bodyHtml;
594
- let m;
595
- while ((m = re.exec(remaining)) !== null) {
596
- hoisted.push(m[1]);
597
- remaining = remaining.slice(m[0].length);
598
- }
589
+ // Shares the leading-run scanner (comment-skipping included) with the
590
+ // streaming path so both hoist identically. See collectHoistedHeadTags.
591
+ const { tags: hoisted, body: remaining } = collectHoistedHeadTags(bodyHtml);
599
592
  if (!hoisted.length) return { head: headHtml, body: bodyHtml };
600
593
  const newHead = headHtml.replace('</head>', hoisted.join('\n') + '\n</head>');
601
594
  return { head: newHead, body: remaining };
@@ -744,14 +737,33 @@ function wrapInDocument(body, opts) {
744
737
  */
745
738
  function collectHoistedHeadTags(bodyHtml) {
746
739
  const tags = [];
747
- const re = /^\s*(<script[\s>][\s\S]*?<\/script>|<style[\s>][\s\S]*?<\/style>|<link\b[^>]*>)/i;
740
+ // <script>…</script> and <style>…</style> are paired; <link …> is void.
741
+ // A plain HTML comment (<!-- … -->) is consumed but NOT hoisted, so a
742
+ // comment interleaved with head-bound tags (e.g. "<!-- Self-hosted fonts -->"
743
+ // between a favicon <link> and the stylesheet <link>) does not terminate
744
+ // the leading run and strand the stylesheet in <body>, which caused FOUC
745
+ // because a <link rel="stylesheet"> in <body> is not reliably
746
+ // render-blocking (#406). The `(?!/?wj:)` guard exempts client-router
747
+ // markers (<!--wj:children:…-->, <!--/wj:children-->) so a layout that
748
+ // renders children directly after its head tags still terminates the run
749
+ // there rather than swallowing the nesting marker.
750
+ const re =
751
+ /^\s*(<!--(?!\/?wj:)[\s\S]*?-->|<script[\s>][\s\S]*?<\/script>|<style[\s>][\s\S]*?<\/style>|<link\b[^>]*>)/i;
748
752
  let remaining = bodyHtml;
753
+ // `body` only advances to just-past the LAST hoisted head tag. Comments
754
+ // are scanned through (so they don't terminate the run) but a comment that
755
+ // trails the final head tag stays in the body rather than being dropped.
756
+ let body = bodyHtml;
749
757
  let m;
750
758
  while ((m = re.exec(remaining)) !== null) {
751
- tags.push(m[1]);
759
+ const token = m[1];
752
760
  remaining = remaining.slice(m[0].length);
761
+ if (!token.startsWith('<!--')) {
762
+ tags.push(token);
763
+ body = remaining;
764
+ }
753
765
  }
754
- return { tags, body: remaining };
766
+ return { tags, body };
755
767
  }
756
768
 
757
769
  /**