Skip to content

[audit] motion-dom/stats: dedupe summarise calls, inline helpers#3742

Open
mattgperry wants to merge 1 commit into
mainfrom
audit/motion-dom-stats
Open

[audit] motion-dom/stats: dedupe summarise calls, inline helpers#3742
mattgperry wants to merge 1 commit into
mainfrom
audit/motion-dom-stats

Conversation

@mattgperry
Copy link
Copy Markdown
Collaborator

Directory

packages/motion-dom/src/stats/ — debug-mode runtime stats collection.
Exports recordStats (called from framer-motion/src/debug.ts and
projection.ts) plus the internal statsBuffer and activeAnimations
that hot-path code (JSAnimation, WAAPI start, projection node)
increments/decrements and conditionally reads from. recordStats is the
only public entry: it allocates buffer arrays, schedules record() on
every postRender frame, and returns a reportStats() closure that
summarises the buffer into a StatsSummary.

Both lenses applied.

Performance

No changes. The per-frame work inside record() is gated on
statsBuffer.value, so it's only active during an explicit
recordStats() profiling session. All hot-path call sites elsewhere
already short-circuit on if (statsBuffer.value) / if (statsBuffer.addProjectionMetrics)
before touching the buffer, so there is no idle-cost to trim. The
reportStats() work (Math.min(...values) / Math.max(...values) /
reduce) runs once when the user ends a session — not a hot path.

Filesize

Measured per-file gzipped deltas after yarn build:

Output Before After Δ
motion-dom/dist/motion-dom.js 38921 38799 −122
motion-dom/dist/motion-dom.dev.js 90813 90655 −158
motion-dom/dist/cjs/index.js 89109 88989 −120
motion-dom/dist/es/stats/index.mjs 997 910 −87
framer-motion/dist/framer-motion.js 60694 60568 −126
framer-motion/dist/dom.js 44678 44568 −110
framer-motion/dist/dom-mini.js 4979 4979 0 (no stats)
motion-dom/dist/es/index.mjs 3094 3094 0 (barrel only)

User-facing bundles drop ~110–126 gzipped bytes each.

Changes (all in reportStats / summarise):

  1. Collapse 15× summarise(value.X.Y) into a generic summariseAll
    helper
    that iterates each sub-object's keys. The summary object
    shrinks from three nested literals (~700 chars pre-min) to three
    helper calls. This is the bulk of the win.
  2. Inline single-use mean() — the calcAverage default
    parameter was never overridden by any caller. Replaced with an
    inline reduce inside summarise.
  3. Inline single-use msToFps() — three call sites become
    Math.round(1000 / x) directly; the explicit array-destructuring
    swap becomes a single temp, which compresses slightly better.
  4. Drop the defensive cancelFrame(record) inside record()
    reportStats() already calls cancelFrame(record) before clearing
    the buffer, so the null branch was unreachable. Replaced with a
    simple if (!value) return.

No public API changed; recordStats / statsBuffer / activeAnimations
signatures are identical.

Verification

  • yarn build clean.
  • yarn test — all stats tests pass (3/3 in motion-dom/src/stats/__tests__/index.test.ts).
    One unrelated AnimatePresence test was flaky on the first run and
    passed on re-run.

🤖 Generated with Claude Code

Replace 15 explicit summarise() calls in reportStats with a small
generic summariseAll helper that maps each sub-object's keys; inline
the single-use mean() and msToFps() helpers; drop the defensive
cancelFrame inside record() (reportStats already cancels before
clearing the buffer).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 28, 2026

Greptile Summary

This PR refactors the debug-mode stats collection module (motion-dom/src/stats/index.ts) to reduce bundle size without any behavioral changes. The public API (recordStats, statsBuffer, activeAnimations) and the StatsSummary shape are untouched.

  • summariseAll helper: replaces 15 explicit summarise(value.X.Y) calls with a generic for...in loop helper, correctly inferring the key-set from the shape of each sub-object passed at the three call sites.
  • Inlined mean and msToFps: the two single-use helpers are folded into their call sites as inline reduce and Math.round(1000/x) expressions; the FPS min/max swap is preserved — the new two-step temp-variable pattern is mathematically equivalent to the old destructuring swap.
  • Removed unreachable cancelFrame in record(): reportStats() already calls cancelFrame(record) synchronously before nulling the buffer, so the null-guard branch inside record() could never fire; replaced with a simple falsy early-return.

Confidence Score: 5/5

Safe to merge — purely mechanical refactoring with no behavioral changes and full test coverage.

Every changed behavior was verified: the FPS min/max swap is mathematically identical to the old destructuring swap, summariseAll iterates the same fixed keys on plain objects, the inlined expressions match their extracted helpers exactly, and the dropped defensive cancelFrame was genuinely unreachable. All three existing tests pass.

No files require special attention.

Important Files Changed

Filename Overview
packages/motion-dom/src/stats/index.ts Clean refactor: collapsed 15× explicit summarise() calls into a summariseAll helper, inlined single-use mean/msToFps helpers, and dropped a provably unreachable defensive cancelFrame branch — logic is preserved and tests pass

Reviews (1): Last reviewed commit: "[audit] motion-dom/stats: dedupe summari..." | Re-trigger Greptile

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.

1 participant