rrweb
rrweb copied to clipboard
feat: add new css parser - postcss
rrweb-snapshot
parses stylesheets to adapt the CSS for playback
In certain cases the parsing wasn't always correct: https://github.com/PostHog/posthog/pull/21427 https://github.com/rrweb-io/rrweb/issues/1379
Which led to improved CSS selector efforts first included in [email protected]
(https://github.com/rrweb-io/rrweb/pull/1401)
Sadly this caused playback issues that consumed 100% CPU and froze playback
https://github.com/PostHog/posthog/issues/21791
CSS parsing is performed in the https://github.com/rrweb-io/rrweb/blob/master/packages/rrweb-snapshot/src/css.ts which was pulled from https://github.com/reworkcss/css (which hasn't been updated in over 3 years)
I propose moving to https://github.com/csstree/csstree which creates an actual AST which can be used to effectively debug via https://astexplorer.net
I couldn't figure out some import issues in the test/integration.test.ts
Evaluation failed: ReferenceError: require is not defined
All test/css.test.ts
tests still pass which confirms this is as good as the trickiest cases previously handled without any of the slowness introduced in https://github.com/rrweb-io/rrweb/pull/1401
⚠️ No Changeset found
Latest commit: cdede94f87cf462c84530e3b996fc4027c324a6b
Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.
This PR includes no changesets
When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types
Click here to learn what changesets are, and how to add one.
Click here if you're a maintainer who wants to add a changeset to this PR
This is great and something I thought we might get to 'some day' after the recent fixes to css.ts.
Would you be able to post a before/after of the built file sizes from this change; i.e.
$ ls -al packages/rrweb/dist/*.js packages/rrweb/dist/record/*.js packages/rrweb/dist/replay/*.js
One of the reasons css.ts was ported over was so that we could tree shake it a bit.
Something isn't right here because I'm getting the same values when running yarn build:dev && ls -al packages/rrweb/dist/*.js packages/rrweb/dist/record/*.js packages/rrweb/dist/replay/*.js
on both branches.
~Possibly need to fix the import errors I mentioned in the description first 🤔 🤷~ Scratch that I got it working at the repo root
Before
-rw-r--r--@ 7 david staff 26568 29 Apr 18:24 packages/rrweb/dist/record/rrweb-record-pack.js
-rw-r--r--@ 7 david staff 10418 29 Apr 18:24 packages/rrweb/dist/record/rrweb-record-pack.min.js
-rw-r--r--@ 7 david staff 165350 29 Apr 18:24 packages/rrweb/dist/record/rrweb-record.js
-rw-r--r--@ 7 david staff 68811 29 Apr 18:24 packages/rrweb/dist/record/rrweb-record.min.js
-rw-r--r--@ 7 david staff 209951 29 Apr 18:24 packages/rrweb/dist/replay/rrweb-replay-unpack.js
-rw-r--r--@ 7 david staff 92849 29 Apr 18:24 packages/rrweb/dist/replay/rrweb-replay-unpack.min.js
-rw-r--r--@ 7 david staff 193751 29 Apr 18:24 packages/rrweb/dist/replay/rrweb-replay.js
-rw-r--r--@ 7 david staff 88049 29 Apr 18:24 packages/rrweb/dist/replay/rrweb-replay.min.js
-rw-r--r--@ 7 david staff 401587 29 Apr 18:24 packages/rrweb/dist/rrweb-all.js
-rw-r--r--@ 7 david staff 170294 29 Apr 18:24 packages/rrweb/dist/rrweb-all.min.js
-rw-r--r--@ 7 david staff 351097 29 Apr 18:24 packages/rrweb/dist/rrweb.js
-rw-r--r--@ 7 david staff 152936 29 Apr 18:24 packages/rrweb/dist/rrweb.min.js
After
-rwxrwxr-x@ 3 david staff 26584 30 Apr 16:33 packages/rrweb/dist/record/rrweb-record-pack.js
-rwxrwxr-x@ 3 david staff 10437 30 Apr 16:33 packages/rrweb/dist/record/rrweb-record-pack.min.js
-rwxrwxr-x@ 3 david staff 157698 30 Apr 16:33 packages/rrweb/dist/record/rrweb-record.js
-rwxrwxr-x@ 3 david staff 68728 30 Apr 16:33 packages/rrweb/dist/record/rrweb-record.min.js
-rwxrwxr-x@ 3 david staff 191575 30 Apr 16:33 packages/rrweb/dist/replay/rrweb-replay-unpack.js
-rwxrwxr-x@ 3 david staff 87904 30 Apr 16:33 packages/rrweb/dist/replay/rrweb-replay-unpack.min.js
-rwxrwxr-x@ 3 david staff 175375 30 Apr 16:33 packages/rrweb/dist/replay/rrweb-replay.js
-rwxrwxr-x@ 3 david staff 83086 30 Apr 16:33 packages/rrweb/dist/replay/rrweb-replay.min.js
-rwxrwxr-x@ 3 david staff 375719 30 Apr 16:33 packages/rrweb/dist/rrweb-all.js
-rwxrwxr-x@ 3 david staff 165248 30 Apr 16:33 packages/rrweb/dist/rrweb-all.min.js
-rwxrwxr-x@ 3 david staff 325229 30 Apr 16:34 packages/rrweb/dist/rrweb.js
-rwxrwxr-x@ 3 david staff 147850 30 Apr 16:34 packages/rrweb/dist/rrweb.min.js
In a significant update here I moved to using postcss
once I figured out how their plugin system works. AST explorer was particularly useful in writing the plugins needed. Here's an example for the media selector: https://astexplorer.net/#/gist/244e2fb4da940df52bf0f4b94277db44/7ccd8e85c2a64282f021b34717b0076c418f61b1
This massively simplifies the adaptCssForReplay
method and is extensible to the point that we can easily improve it by adding more custom plugins.
I've moved around some test files to better separate concerns. Some of the tests were "testing the framework" given they had been copied over from https://github.com/reworkcss/css/blob/master/test/parse.js. I removed any that were no longer relevant but left any of the cases added in https://github.com/rrweb-io/rrweb/pull/1440/files#diff-ca07555d1c1965f9c7ef3b5df3772a19953138b8eac62deb7786e549b25b455d as we know they've specifically broken in the past and we would like to avoid breaking them in the future
Super! What sort of filesize numbers are there with the postcss
version?
Sorry @eoghanmurray, probably wasn't clear but I updated my earlier comment with latest build numbers having run yarn build:dev && ls -al packages/rrweb/dist/*.js packages/rrweb/dist/record/*.js packages/rrweb/dist/replay/*.js
master
-rw-r--r--@ 7 david staff 26568 29 Apr 18:24 packages/rrweb/dist/record/rrweb-record-pack.js
-rw-r--r--@ 7 david staff 10418 29 Apr 18:24 packages/rrweb/dist/record/rrweb-record-pack.min.js
-rw-r--r--@ 7 david staff 165350 29 Apr 18:24 packages/rrweb/dist/record/rrweb-record.js
-rw-r--r--@ 7 david staff 68811 29 Apr 18:24 packages/rrweb/dist/record/rrweb-record.min.js
-rw-r--r--@ 7 david staff 209951 29 Apr 18:24 packages/rrweb/dist/replay/rrweb-replay-unpack.js
-rw-r--r--@ 7 david staff 92849 29 Apr 18:24 packages/rrweb/dist/replay/rrweb-replay-unpack.min.js
-rw-r--r--@ 7 david staff 193751 29 Apr 18:24 packages/rrweb/dist/replay/rrweb-replay.js
-rw-r--r--@ 7 david staff 88049 29 Apr 18:24 packages/rrweb/dist/replay/rrweb-replay.min.js
-rw-r--r--@ 7 david staff 401587 29 Apr 18:24 packages/rrweb/dist/rrweb-all.js
-rw-r--r--@ 7 david staff 170294 29 Apr 18:24 packages/rrweb/dist/rrweb-all.min.js
-rw-r--r--@ 7 david staff 351097 29 Apr 18:24 packages/rrweb/dist/rrweb.js
-rw-r--r--@ 7 david staff 152936 29 Apr 18:24 packages/rrweb/dist/rrweb.min.js
fix/css-selectors
-rwxrwxr-x@ 3 david staff 26584 30 Apr 16:33 packages/rrweb/dist/record/rrweb-record-pack.js
-rwxrwxr-x@ 3 david staff 10437 30 Apr 16:33 packages/rrweb/dist/record/rrweb-record-pack.min.js
-rwxrwxr-x@ 3 david staff 157698 30 Apr 16:33 packages/rrweb/dist/record/rrweb-record.js
-rwxrwxr-x@ 3 david staff 68728 30 Apr 16:33 packages/rrweb/dist/record/rrweb-record.min.js
-rwxrwxr-x@ 3 david staff 191575 30 Apr 16:33 packages/rrweb/dist/replay/rrweb-replay-unpack.js
-rwxrwxr-x@ 3 david staff 87904 30 Apr 16:33 packages/rrweb/dist/replay/rrweb-replay-unpack.min.js
-rwxrwxr-x@ 3 david staff 175375 30 Apr 16:33 packages/rrweb/dist/replay/rrweb-replay.js
-rwxrwxr-x@ 3 david staff 83086 30 Apr 16:33 packages/rrweb/dist/replay/rrweb-replay.min.js
-rwxrwxr-x@ 3 david staff 375719 30 Apr 16:33 packages/rrweb/dist/rrweb-all.js
-rwxrwxr-x@ 3 david staff 165248 30 Apr 16:33 packages/rrweb/dist/rrweb-all.min.js
-rwxrwxr-x@ 3 david staff 325229 30 Apr 16:34 packages/rrweb/dist/rrweb.js
-rwxrwxr-x@ 3 david staff 147850 30 Apr 16:34 packages/rrweb/dist/rrweb.min.js
Hopefully the following doesn't derail the conversation, and even if we go ahead with the following, that doesn't detract from the worth of the current PR...
So okay I just had a thought; all this CSS processing is on the replay side, right? I'm wondering why we don't just iterate over the final sheet.rules directly after the stylesheet has been rendered. We will be rendering it as an inline
===
edit: I've drafted this up in https://github.com/rrweb-io/rrweb/pull/1480
a 'versioned' url and a permanent web presence.
I don't think we can know that something has a permanent web presence only because it has a versioned URL - e.g. I was just dealing with a customer bug report that centred around the assets only living for 15 days and not inlining at capture
We either need very robust inlining or asset capture at recording time (ideally both :)). Loading over the net back to the customer is only a fallback in a perfect world.
(I appreciate that's a (this) consumer of the library position and not necessarily a developer of the library position 😊)
Yes, this could be configurable, and likely a robust solution will involve deploy of your own proxy/cache for assets. We replicate the '.hover' modification server side in our proxy cache so applying those modifications at replay on all .sheet
(rather than only the captured cssText) would also make a web proxy easier to run.
Rebase or merge of master needed: there is a new test in #1481 which adds coverage to the css parser; hopefully it will pass (or fail) as-is without having to modify the test itself.
Also re. rrweb-snapshot/test/rebuild.test.ts
if you revert the whitespace changes to the expected css, you could instead cherry-pick the following commit which should do loose matching by ignoring whitespace and semicolon: https://github.com/rrweb-io/rrweb/pull/1480/commits/85db6c21583746637e7c54b9b5782fe0f88f4868
Let me know if you wish me to execute the above two for you. Still awaiting someone to figure out how to pull in a typescript version of https://github.com/giuseppeg/postcss-pseudo-classes
@eoghanmurray cleaned up the whitespacing but bringing in the commit you mentioned. Also rebased to include the new test (it passed without changes). The current failing test seems unrelated but perhaps you can run it again
@Juice10 thanks for fixing up the imports. Do you know if the failing test is related to my changes somehow?
@daibhin failing test was for https://github.com/rrweb-io/rrweb/pull/1458/commits/2872d0ee4fa0455b76836a6616317b561c330344 was due to an upgraded package, thats fixed now (by locking the package version). The failure for https://github.com/rrweb-io/rrweb/pull/1458/commits/c7153b7620a630eaadeee0159d0cc7b1767516c2 seems to be due to jest being extended where we switched over to vitest. I just fixed that issue, but it looks like a chunk of the :hover tests are failing (you'll see that soon for https://github.com/rrweb-io/rrweb/pull/1458/commits/9df9946d7befa28a9d2633aecbce5e6ca11be9cf )