Skip to content

Remove postcss from dist/record.js (it was only intended for replay)#1784

Open
eoghanmurray wants to merge 13 commits intorrweb-io:masterfrom
eoghanmurray:remove-postcss
Open

Remove postcss from dist/record.js (it was only intended for replay)#1784
eoghanmurray wants to merge 13 commits intorrweb-io:masterfrom
eoghanmurray:remove-postcss

Conversation

@eoghanmurray
Copy link
Contributor

@eoghanmurray eoghanmurray commented Feb 6, 2026

This succeeds #1743

Thanks to #1630 we should be able to see the effect of this in the CI as part of this PR

Tree-shaking postcss

The rrweb-snapshot package contains both snapshot (record-time) and rebuild
(replay-time) code. The rebuild module imports postcss, a large dependency
that is only needed during replay. Because rrweb-snapshot re-exports
everything from a single index.ts barrel file, bundlers that consume the
pre-built package see it as one indivisible chunk — they cannot tree-shake out
rebuild.ts and its postcss import, even when only snapshot functions are
used.

We initially tried adding /*#__PURE__*/ annotations to side-effecting
expressions (e.g. new RegExp(...)) inside rrweb-snapshot/src/css.ts, hoping
Rollup would then drop the unused modules. This did not work because by the
time this package imports from the published rrweb-snapshot package, the
sources have already been bundled together — the individual module boundaries
that Rollup needs for tree-shaking no longer exist.

The solution (in vite.config.ts) uses two techniques:

  1. A custom resolveId plugin redirects imports of rrweb, rrweb-snapshot,
    and rrdom to their original source entry points instead of the
    pre-built packages. This restores individual module boundaries so Rollup can
    see exactly which source files are used and which are not.

  2. treeshake.moduleSideEffects: false [update - moved to "sideEffects": false, in rrweb-snapshot/package.json] tells Rollup it is safe to drop
    any module [the rrweb-snapshot modules] whose exports are not consumed, even if that module contains
    top-level code. This allows rebuild.ts (and therefore postcss) to be
    eliminated entirely from the record bundle.

Additionally, packages/rrweb/src/entries/record.ts was changed from a
default-import-then-re-export pattern to direct named re-exports, which gives
Rollup a clearer view of what is actually used.

A test in packages/record/test/record.test.ts asserts that no output bundle
file contains the string "postcss", guarding against future regressions.

Co-Authored by Claude AI

eoghanmurray and others added 5 commits February 6, 2026 13:36
…output - postcss is only needed at record time
…t; we use a stub here to ensure thta the `hasShadowRoot` function still works (`n instanceof BaseRRNode` will always be false)
@changeset-bot
Copy link

changeset-bot bot commented Feb 6, 2026

🦋 Changeset detected

Latest commit: 2b78153

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

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

@eoghanmurray
Copy link
Contributor Author

Size Change: -823 kB (-8.15%) ✅

Total Size: 9.27 MB

Filename Size Change
packages/record/dist/record.cjs 159 kB -238 kB (-59.91%) 🏆
packages/record/dist/record.js 159 kB -238 kB (-59.92%) 🏆
packages/record/dist/record.umd.cjs 161 kB -240 kB (-59.84%) 🏆
packages/record/dist/record.umd.min.cjs 76.5 kB -107 kB (-58.41%) 🏆

@eoghanmurray eoghanmurray changed the title Remove postcss Remove postcss from dist/record.js (it was only intended for replay) Feb 6, 2026
…_request_target` permission which will allow the check to write back into the PR directly

Q: are there any new 'heavy' steps added here in comparison to where it has been moved from?

Claude AI: No. The compressed-size-action does its own install and build internally via the install-script and build-script parameters — it checks out both the base and PR branches, installs, and builds each to compare sizes. That's the same work it was doing in ci-cd.yml.
The only new steps are actions/checkout and actions/setup-node, which are lightweight.
In fact, in ci-cd.yml it was arguably more wasteful — the job had already done a full yarn install and yarn build:all before the action ran its own install+build again via the parameters.
eoghanmurray added a commit to eoghanmurray/rrweb that referenced this pull request Feb 6, 2026
…-io#1784 - adding here also to show how the conflicts would resolve
@Juice10 Juice10 self-assigned this Feb 10, 2026
Copy link
Member

Choose a reason for hiding this comment

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

Let's try and generalize the changes here and move them to /vite.config.default.ts.
One of the reasons to move towards a unified build process was to reduce package specific bundle code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, but I think this could also be solved with a more in-depth refactor of rrweb-snapshot, to make a cleaner separation of snapshot and rebuild functionality (specifically not having a shared utils.ts for both sides)

The packages/record and packages/replay are intended to be the final destination for the packages/rrweb/src/record and packages/rrweb/src/replay code, so that could help too in that we could then remove this 'hack'. From that pov I'd vote to go forward with the exceptions in this PR only living in the ./packages/record folder and checking after those refactors whether we can remove

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So in latest the `rollupOptions are gone (commented out) from record/vite.config.ts

That just leaves recordOnlyResolvePlugin as the main customization in that config.

Rollup plugin that redirects workspace package imports to their source entry
points during JS bundling. This lets Rollup see individual source modules
and tree-shake out replay-only code (rebuild.ts / postcss).

Is this a candidate for moving to the top level? As I understand it, it would need to be rewritten as 'resolve to individual source modules instead of the 'barrel' outputs? Is this a known technique?

Comment on lines +52 to +54
// Allow Rollup to drop modules whose exports are unused, even
// if they contain top-level code (e.g. rebuild.ts / postcss).
moduleSideEffects: false,
Copy link
Member

Choose a reason for hiding this comment

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

How do we know this blanket statement doesn't have any unintended consequences?

Copy link
Member

Choose a reason for hiding this comment

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

I had AI go through the whole repo, it seems like the risk is mostly minimal/hypothetical currently.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I moved it up to root to see how it affects build for all projects;
It won't work for rrweb-player as it results in the Sveldt library being dropped from the output:

You get a diff e.g. in rrweb-player.cjs where the following bit is removed:

  const PUBLIC_VERSION = "4";                                                                                                                                                           
  if (typeof window !== "undefined")                                                                                                                                                    
    (window.__svelte || (window.__svelte = { v: new Set() })).v.add(PUBLIC_VERSION);           
[+ many many lines]                                                                                     

Presumably that's Svelte making itself available (a side effect!).

There are also changes to the chrome extension, presumably because it pulls in rrweb-player but I didn't check

Copy link
Contributor Author

Choose a reason for hiding this comment

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

One other change (maybe minor, maybe not) which affects all build outputs in all packages is the addition of a /* @__PURE */ marker to certain parts, e.g. in packages/all/dist/all.cjs:

var stringify_1$1;
var hasRequiredStringify$1;
function requireStringify$1() {
  if (hasRequiredStringify$1) return stringify_1$1;
  hasRequiredStringify$1 = 1;
  let Stringifier = /* @__PURE__ */ requireStringifier$1();
  function stringify(node2, builder) {
    let str = new Stringifier(builder);
    str.stringify(node2);
  }
  stringify_1$1 = stringify;
  stringify.default = stringify;
  return stringify_1$1;
}
var node$1;
var hasRequiredNode$1;
function requireNode$1() {
  if (hasRequiredNode$1) return node$1;
  hasRequiredNode$1 = 1;
  let { isClean, my } = /* @__PURE__ */ requireSymbols$1();
  let CssSyntaxError = /* @__PURE__ */ requireCssSyntaxError$1();
  let Stringifier = /* @__PURE__ */ requireStringifier$1();
  let stringify = /* @__PURE__ */ requireStringify$1();
  function cloneNode(obj, parent) {
    let cloned = new obj.constructor();
    for (let i2 in obj) {
      if (!Object.prototype.hasOwnProperty.call(obj, i2)) {
        continue;

I don't know if this is also a problem?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How do we know this blanket statement doesn't have any unintended consequences?

We know from checking (at this point in time of the codebase) that it doesn't have any unintended consequences for the record package — perhaps it might have an unintended consequence in future, but that will be discovered as part of any PR that introduces a side-effecting-module for the record side by the record/dist/ output not working (assuming we are using the record package for testing). I think this could be a good thing that we aggressively tree shake packages/record by default?

For now anyhow I've removed this blanket statement, but moved it into the rrweb-snapshot package.json instead as sideEffects: false there.

I've added notes in record/vite.config.ts and also in rrweb-snapshot/BUILD.md to document this.

…ackage so that it's more targeted - that's the only material affect the blanket version was affecting
Juice10 added a commit that referenced this pull request Feb 13, 2026
…1787)

* Update filesize badges (might need further evolution before 2.0.0)

* Don't run full CI/CD when only .md docs have changed in the PR

 - move eslint checks into their own file so they can also ignore .md changes
 - prettier checks don't need the same perms as eslint, so we can demote pull_request_target -> pull_request

* Add empty changeset

* Implement the bundle size change originally originally added in #1784 - adding here also to show how the conflicts would resolve

* Update .github/workflows/eslint-check.yml

---------

Co-authored-by: Justin Halsall <Juice10@users.noreply.github.com>
@github-actions
Copy link
Contributor

Size Change: -1.06 MB (-10.24%) 👏

Total Size: 9.27 MB

Filename Size Change
packages/all/dist/all.cjs 578 kB +20.6 kB (+3.71%)
packages/all/dist/all.js 577 kB +20.6 kB (+3.71%)
packages/all/dist/all.umd.cjs 581 kB +20.6 kB (+3.68%)
packages/all/dist/all.umd.min.cjs 273 kB +1.27 kB (+0.47%)
packages/packer/dist/base-B40z8PPs.cjs 0 B -18.3 kB (removed) 🏆
packages/packer/dist/base-B40z8PPs.umd.cjs 0 B -19.4 kB (removed) 🏆
packages/packer/dist/base-B40z8PPs.umd.min.cjs 0 B -10.1 kB (removed) 🏆
packages/packer/dist/base-BrE4jft0.js 0 B -18.2 kB (removed) 🏆
packages/packer/dist/base-DF-ifV6c.cjs 18.3 kB +18.3 kB (new file) 🆕
packages/packer/dist/base-DF-ifV6c.umd.cjs 19.4 kB +19.4 kB (new file) 🆕
packages/packer/dist/base-DF-ifV6c.umd.min.cjs 10.1 kB +10.1 kB (new file) 🆕
packages/packer/dist/base-Dgki_PiJ.js 18.2 kB +18.2 kB (new file) 🆕
packages/record/dist/record.cjs 159 kB -218 kB (-57.78%) 🏆
packages/record/dist/record.js 159 kB -218 kB (-57.8%) 🏆
packages/record/dist/record.umd.cjs 161 kB -220 kB (-57.73%) 🏆
packages/record/dist/record.umd.min.cjs 76.5 kB -106 kB (-58.1%) 🏆
packages/replay/dist/replay.cjs 410 kB +20.9 kB (+5.37%) 🔍
packages/replay/dist/replay.js 410 kB +20.9 kB (+5.38%) 🔍
packages/replay/dist/replay.umd.cjs 414 kB +20.9 kB (+5.33%) 🔍
packages/replay/dist/replay.umd.min.cjs 196 kB +1.51 kB (+0.78%)
packages/rrdom-nodejs/dist/rrdom-nodejs.cjs 143 kB +11.1 kB (+8.42%) 🔍
packages/rrdom-nodejs/dist/rrdom-nodejs.js 142 kB +11.1 kB (+8.44%) 🔍
packages/rrdom-nodejs/dist/rrdom-nodejs.umd.cjs 145 kB +11.1 kB (+8.25%) 🔍
packages/rrdom-nodejs/dist/rrdom-nodejs.umd.min.cjs 68.2 kB +1.25 kB (+1.87%)
packages/rrdom/dist/rrdom.cjs 161 kB +11.3 kB (+7.52%) 🔍
packages/rrdom/dist/rrdom.js 160 kB +11.3 kB (+7.55%) 🔍
packages/rrdom/dist/rrdom.umd.cjs 163 kB +11.2 kB (+7.4%) 🔍
packages/rrdom/dist/rrdom.umd.min.cjs 75.3 kB +1.24 kB (+1.67%)
packages/rrweb-player/dist/rrweb-player.cjs 294 kB -167 kB (-36.21%) 🎉
packages/rrweb-player/dist/rrweb-player.js 294 kB -167 kB (-36.22%) 🎉
packages/rrweb-player/dist/rrweb-player.umd.cjs 296 kB -168 kB (-36.11%) 🎉
packages/rrweb-player/dist/rrweb-player.umd.min.cjs 132 kB -91.1 kB (-40.74%) 🎉
packages/rrweb-snapshot/dist/rrweb-snapshot.cjs 176 kB +11.4 kB (+6.91%) 🔍
packages/rrweb-snapshot/dist/rrweb-snapshot.js 175 kB +11.3 kB (+6.93%) 🔍
packages/rrweb-snapshot/dist/rrweb-snapshot.umd.cjs 179 kB +11.4 kB (+6.79%) 🔍
packages/rrweb-snapshot/dist/rrweb-snapshot.umd.min.cjs 83.3 kB +1.32 kB (+1.61%)
packages/rrweb/dist/rrweb.cjs 560 kB +20.7 kB (+3.84%)
packages/rrweb/dist/rrweb.js 560 kB +20.7 kB (+3.84%)
packages/rrweb/dist/rrweb.umd.cjs 561 kB +20.7 kB (+3.82%)
packages/rrweb/dist/rrweb.umd.min.cjs 264 kB +1.35 kB (+0.51%)
packages/utils/dist/utils.cjs 6.25 kB +186 B (+3.07%)
packages/utils/dist/utils.js 5.74 kB +164 B (+2.94%)
packages/utils/dist/utils.umd.cjs 7.28 kB +186 B (+2.62%)
packages/utils/dist/utils.umd.min.cjs 4.09 kB +105 B (+2.63%)
ℹ️ View Unchanged
Filename Size
packages/packer/dist/pack.cjs 347 B
packages/packer/dist/pack.js 285 B
packages/packer/dist/pack.umd.cjs 2.25 kB
packages/packer/dist/pack.umd.min.cjs 1.73 kB
packages/packer/dist/packer.cjs 257 B
packages/packer/dist/packer.js 136 B
packages/packer/dist/packer.umd.cjs 1.28 kB
packages/packer/dist/packer.umd.min.cjs 1.25 kB
packages/packer/dist/unpack.cjs 769 B
packages/packer/dist/unpack.js 702 B
packages/packer/dist/unpack.umd.cjs 1.79 kB
packages/packer/dist/unpack.umd.min.cjs 1.57 kB
packages/plugins/rrweb-plugin-canvas-webrtc-record/dist/rrweb-plugin-canvas-webrtc-record.cjs 37.6 kB
packages/plugins/rrweb-plugin-canvas-webrtc-record/dist/rrweb-plugin-canvas-webrtc-record.js 37.5 kB
packages/plugins/rrweb-plugin-canvas-webrtc-record/dist/rrweb-plugin-canvas-webrtc-record.umd.cjs 38.7 kB
packages/plugins/rrweb-plugin-canvas-webrtc-record/dist/rrweb-plugin-canvas-webrtc-record.umd.min.cjs 22.9 kB
packages/plugins/rrweb-plugin-canvas-webrtc-replay/dist/rrweb-plugin-canvas-webrtc-replay.cjs 34.3 kB
packages/plugins/rrweb-plugin-canvas-webrtc-replay/dist/rrweb-plugin-canvas-webrtc-replay.js 34.2 kB
packages/plugins/rrweb-plugin-canvas-webrtc-replay/dist/rrweb-plugin-canvas-webrtc-replay.umd.cjs 35.4 kB
packages/plugins/rrweb-plugin-canvas-webrtc-replay/dist/rrweb-plugin-canvas-webrtc-replay.umd.min.cjs 21.2 kB
packages/plugins/rrweb-plugin-console-record/dist/rrweb-plugin-console-record.cjs 15 kB
packages/plugins/rrweb-plugin-console-record/dist/rrweb-plugin-console-record.js 14.9 kB
packages/plugins/rrweb-plugin-console-record/dist/rrweb-plugin-console-record.umd.cjs 16.1 kB
packages/plugins/rrweb-plugin-console-record/dist/rrweb-plugin-console-record.umd.min.cjs 8.03 kB
packages/plugins/rrweb-plugin-console-replay/dist/rrweb-plugin-console-replay.cjs 5.01 kB
packages/plugins/rrweb-plugin-console-replay/dist/rrweb-plugin-console-replay.js 4.9 kB
packages/plugins/rrweb-plugin-console-replay/dist/rrweb-plugin-console-replay.umd.cjs 6.07 kB
packages/plugins/rrweb-plugin-console-replay/dist/rrweb-plugin-console-replay.umd.min.cjs 3.27 kB
packages/plugins/rrweb-plugin-sequential-id-record/dist/rrweb-plugin-sequential-id-record.cjs 681 B
packages/plugins/rrweb-plugin-sequential-id-record/dist/rrweb-plugin-sequential-id-record.js 548 B
packages/plugins/rrweb-plugin-sequential-id-record/dist/rrweb-plugin-sequential-id-record.umd.cjs 1.76 kB
packages/plugins/rrweb-plugin-sequential-id-record/dist/rrweb-plugin-sequential-id-record.umd.min.cjs 1.47 kB
packages/plugins/rrweb-plugin-sequential-id-replay/dist/rrweb-plugin-sequential-id-replay.cjs 933 B
packages/plugins/rrweb-plugin-sequential-id-replay/dist/rrweb-plugin-sequential-id-replay.js 820 B
packages/plugins/rrweb-plugin-sequential-id-replay/dist/rrweb-plugin-sequential-id-replay.umd.cjs 2.01 kB
packages/plugins/rrweb-plugin-sequential-id-replay/dist/rrweb-plugin-sequential-id-replay.umd.min.cjs 1.61 kB
packages/replay/dist/style.css 2.45 kB
packages/replay/dist/style.min.css 1.97 kB
packages/rrweb-player/dist/events.js 159 kB
packages/rrweb-player/dist/global.css 240 B
packages/rrweb-player/dist/style.css 5.57 kB
packages/rrweb-player/dist/style.min.css 5 kB
packages/rrweb/dist/style.css 2.45 kB
packages/rrweb/dist/style.min.css 1.97 kB
packages/types/dist/types.cjs 5.63 kB
packages/types/dist/types.js 5.38 kB
packages/types/dist/types.umd.cjs 6.66 kB
packages/types/dist/types.umd.min.cjs 3.41 kB

compressed-size-action

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.

2 participants