Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@AndrewJakubowicz
Copy link
Contributor

@AndrewJakubowicz AndrewJakubowicz commented Jan 25, 2024

Fixes: #4513

Context

Using parse from parse5 everywhere for server templates is not ideal, and unfortunately has consequences for some cases such as the issue template containing a top level td element. Parsing this as a page level HTML causes the element to be removed (which matches browser behavior). However, we instead want to treat this case as a fragment.

Fix

This is a possible fix. I'm not super stoked about it but it does improve the situation.

Basically, there are 3 tags that I could find that parseFragment will not handle as a top-level element: html, head, and body.
Thus, detect those cases and only use parse if one of these top-level tags is detected. Otherwise use parseFragment.

This change also strips off comment nodes.

Test plan

Added a regression test for the template flagged in the issue.

@changeset-bot
Copy link

changeset-bot bot commented Jan 25, 2024

🦋 Changeset detected

Latest commit: 2c55b58

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@lit-labs/ssr Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Contributor

github-actions bot commented Jan 25, 2024

📊 Tachometer Benchmark Results

Summary

nop-update

  • this-change, tip-of-tree, previous-release: unsure 🔍 -6% - +2% (-0.71ms - +0.25ms)
    this-change vs tip-of-tree

render

  • this-change: 51.03ms - 53.66ms
  • this-change, tip-of-tree, previous-release: unsure 🔍 -3% - +4% (-0.64ms - +0.73ms)
    this-change vs tip-of-tree
  • this-change, tip-of-tree, previous-release: unsure 🔍 -3% - +2% (-0.97ms - +0.58ms)
    this-change vs tip-of-tree
  • this-change, tip-of-tree, previous-release: unsure 🔍 -4% - +1% (-1.38ms - +0.36ms)
    this-change vs tip-of-tree

update

  • this-change: 571.17ms - 583.01ms
  • this-change, tip-of-tree, previous-release: unsure 🔍 -7% - +6% (-3.10ms - +2.68ms)
    this-change vs tip-of-tree
  • this-change, tip-of-tree, previous-release: unsure 🔍 -2% - +3% (-1.42ms - +2.49ms)
    this-change vs tip-of-tree
  • this-change, tip-of-tree, previous-release: unsure 🔍 -2% - +1% (-9.13ms - +5.52ms)
    this-change vs tip-of-tree

update-reflect

  • this-change: 569.76ms - 575.96ms
  • this-change, tip-of-tree, previous-release: unsure 🔍 -1% - +2% (-4.00ms - +9.52ms)
    this-change vs tip-of-tree

Results

this-change

render

VersionAvg timevs
51.03ms - 53.66ms-

update

VersionAvg timevs
571.17ms - 583.01ms-

update-reflect

VersionAvg timevs
569.76ms - 575.96ms-
this-change, tip-of-tree, previous-release

render

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
18.67ms - 19.66ms-unsure 🔍
-3% - +4%
-0.64ms - +0.73ms
unsure 🔍
-7% - +0%
-1.40ms - +0.09ms
tip-of-tree
tip-of-tree
18.64ms - 19.60msunsure 🔍
-4% - +3%
-0.73ms - +0.64ms
-unsure 🔍
-7% - +0%
-1.43ms - +0.03ms
previous-release
previous-release
19.26ms - 20.37msunsure 🔍
-1% - +7%
-0.09ms - +1.40ms
unsure 🔍
-0% - +8%
-0.03ms - +1.43ms
-

update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
41.26ms - 44.89ms-unsure 🔍
-7% - +6%
-3.10ms - +2.68ms
faster ✔
3% - 13%
1.16ms - 6.23ms
tip-of-tree
tip-of-tree
41.04ms - 45.53msunsure 🔍
-6% - +7%
-2.68ms - +3.10ms
-faster ✔
2% - 13%
0.63ms - 6.34ms
previous-release
previous-release
45.01ms - 48.53msslower ❌
2% - 15%
1.16ms - 6.23ms
slower ❌
1% - 15%
0.63ms - 6.34ms
-

nop-update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
11.36ms - 12.08ms-unsure 🔍
-6% - +2%
-0.71ms - +0.25ms
faster ✔
0% - 8%
0.00ms - 1.04ms
tip-of-tree
tip-of-tree
11.62ms - 12.28msunsure 🔍
-2% - +6%
-0.25ms - +0.71ms
-unsure 🔍
-6% - +2%
-0.79ms - +0.21ms
previous-release
previous-release
11.86ms - 12.62msunsure 🔍
-0% - +9%
+0.00ms - +1.04ms
unsure 🔍
-2% - +7%
-0.21ms - +0.79ms
-
this-change, tip-of-tree, previous-release

render

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
34.70ms - 35.80ms-unsure 🔍
-3% - +2%
-0.97ms - +0.58ms
unsure 🔍
-3% - +1%
-1.12ms - +0.49ms
tip-of-tree
tip-of-tree
34.91ms - 35.99msunsure 🔍
-2% - +3%
-0.58ms - +0.97ms
-unsure 🔍
-3% - +2%
-0.92ms - +0.68ms
previous-release
previous-release
34.98ms - 36.15msunsure 🔍
-1% - +3%
-0.49ms - +1.12ms
unsure 🔍
-2% - +3%
-0.68ms - +0.92ms
-

update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
78.84ms - 81.63ms-unsure 🔍
-2% - +3%
-1.42ms - +2.49ms
unsure 🔍
-5% - +2%
-3.86ms - +1.73ms
tip-of-tree
tip-of-tree
78.33ms - 81.07msunsure 🔍
-3% - +2%
-2.49ms - +1.42ms
-unsure 🔍
-5% - +1%
-4.38ms - +1.18ms
previous-release
previous-release
78.88ms - 83.72msunsure 🔍
-2% - +5%
-1.73ms - +3.86ms
unsure 🔍
-2% - +6%
-1.18ms - +4.38ms
-
this-change, tip-of-tree, previous-release

render

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
36.03ms - 36.95ms-unsure 🔍
-4% - +1%
-1.38ms - +0.36ms
unsure 🔍
-2% - +1%
-0.88ms - +0.52ms
tip-of-tree
tip-of-tree
36.27ms - 37.73msunsure 🔍
-1% - +4%
-0.36ms - +1.38ms
-unsure 🔍
-2% - +3%
-0.57ms - +1.23ms
previous-release
previous-release
36.14ms - 37.20msunsure 🔍
-1% - +2%
-0.52ms - +0.88ms
unsure 🔍
-3% - +2%
-1.23ms - +0.57ms
-

update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
595.53ms - 605.22ms-unsure 🔍
-2% - +1%
-9.13ms - +5.52ms
unsure 🔍
-1% - +1%
-6.19ms - +7.30ms
tip-of-tree
tip-of-tree
596.69ms - 607.67msunsure 🔍
-1% - +2%
-5.52ms - +9.13ms
-unsure 🔍
-1% - +2%
-4.86ms - +9.59ms
previous-release
previous-release
595.13ms - 604.51msunsure 🔍
-1% - +1%
-7.30ms - +6.19ms
unsure 🔍
-2% - +1%
-9.59ms - +4.86ms
-

update-reflect

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
600.66ms - 610.38ms-unsure 🔍
-1% - +2%
-4.00ms - +9.52ms
unsure 🔍
-1% - +1%
-4.78ms - +7.45ms
tip-of-tree
tip-of-tree
598.06ms - 607.46msunsure 🔍
-2% - +1%
-9.52ms - +4.00ms
-unsure 🔍
-1% - +1%
-7.42ms - +4.56ms
previous-release
previous-release
600.48ms - 607.90msunsure 🔍
-1% - +1%
-7.45ms - +4.78ms
unsure 🔍
-1% - +1%
-4.56ms - +7.42ms
-

tachometer-reporter-action v2 for Benchmarks

@github-actions
Copy link
Contributor

The size of lit-html.js and lit-core.min.js are as expected.

@justinfagnani
Copy link
Collaborator

I suspect there's a bit more we can do here with more advanced parts of the parse5 API. Looking at the implementation of parseFragment() it seems like we can get at the root node and see attributes on it:

import {Parser} from 'parse5';

const parser = Parser.getFragmentParser();
parser.tokenizer.write('<html foo><div></div></html>', true);

// This has the `foo` attribute
console.log(parser.treeAdapter.getFirstChild(parser.document));

// This only has the <div>
console.log(parser.getFragment());

I'm not sure yet how to tie this into a fix, but it seems like more of the information is there internally.

@AndrewJakubowicz
Copy link
Contributor Author

I agree Justin! I'll take a look a little more deeply into what parse5 gives us tomorrow.

@AndrewJakubowicz
Copy link
Contributor Author

@justinfagnani I think an inexpensive regex approach is probably going to be the best to capture the couple page level elements. Using parseFragment specifically on templates containing a top level <html> doesn't work well (even if manually rigged up) because parse5 removes the sourceCodeLocation. I haven't found a way to populate the source code location, which is important because we use it to flushTo and skipTo around Lit markers.

Copy link
Member

@augustjk augustjk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless we unship the parse() change to regress on the page level element support, I like this is a strict improvement of what we currently have.

Trying to not use parse() all together could be explored on top of this change too.

* we use a page-level template `parse`.
*/
const REGEXP_TEMPLATE_STARTS_WITH_PAGE_TAG =
/^\s*(<(!doctype|html|head|body))/i;
Copy link
Collaborator

@justinfagnani justinfagnani Jan 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will handle leading comments too:

Suggested change
/^\s*(<(!doctype|html|head|body))/i;
/^(\s*|<!--.*-->)*(<(!doctype|html|head|body))/is;

This would need some test coverage, obviously!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great suggestion. I've updated regex and added a bunch of tests. Things look pretty great with the comment regex addition.

@justinfagnani
Copy link
Collaborator

@AndrewJakubowicz thanks for looking into it!

I think the limitations of using non-fragment should be documented along with the tag. I modified the regex to handle comments (I think) so maybe there wouldn't be any to mention.

Copy link
Member

@augustjk augustjk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@AndrewJakubowicz AndrewJakubowicz merged commit 8d3e305 into main Jan 29, 2024
@AndrewJakubowicz AndrewJakubowicz deleted the server-html-templates-td branch January 29, 2024 18:36
@lit-robot lit-robot mentioned this pull request Jan 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[labs/ssr] 3.2.0 to 3.2.1 breaks <td colspan=${'foo')> in server html template

3 participants