Skip to content

perf(Message): hoist regex compilation in message text rendering - #3202

Merged
oliverlaz merged 2 commits into
masterfrom
perf/hoist-emoji-regex
Jun 3, 2026
Merged

perf(Message): hoist regex compilation in message text rendering#3202
oliverlaz merged 2 commits into
masterfrom
perf/hoist-emoji-regex

Conversation

@oliverlaz

@oliverlaz oliverlaz commented Jun 3, 2026

Copy link
Copy Markdown
Member

🎯 Goal

Eliminate repeated RegExp compilation in the message text–rendering hot path* from the Vercel React best-practices audit (PERFORMANCE_AUDIT.md, js-hoist-regexp). These regexes were recompiled per render / per renderText call; they are now compiled once at module load.

🛠 Implementation details

Two commits:

  • perf(Message): reuse one compiled emoji regex … emoji-regex compiles a ~15KB source string on every emojiRegex() call. countEmojis / messageTextHasEmojisOnly (Message/utils.tsx) and the emoji markdown rehype plugin (renderText/rehypePlugins/emojiMarkdownPlugin.ts) each built their own instance per call. Introduced src/components/Message/emojiRegex.ts exporting a single shared EMOJI_REGEX, imported by both. Used a dedicated leaf module (rather than re-exporting from utils.tsx) so the lightweight renderText plugin doesn't pull in utils.tsx's heavier module graph or risk a circular import.
  • perf(Message): hoist renderText code-block and markdown-link regexes messageCodeBlocks / matchMarkdownLinks (renderText/regex.ts) created their RegExp literals on every call. Hoisted codeRegex, regexMdLinks, and singleMatch to module scope, matching the existing detectHttp pattern in the same file.

Safety (shared global regex): the emoji regex is global, so it carries a mutable lastIndex. Every consumer resets lastIndex before scanning — String#match/#replace do so internally, and hast-util-find-and-replace resets it before each use (lib/index.js:155). A new test, renderText/__tests__/emojiMarkdownPlugin.test.ts (4 tests), guards this — including one that poisons lastIndex and asserts findAndReplace resets it, so a future version of that library can't silently break the shared instance.

oliverlaz added 2 commits June 3, 2026 12:43
…Text

countEmojis/messageTextHasEmojisOnly and the emoji markdown rehype plugin each
compiled their own ~15KB emoji RegExp on every call. Build it once in a shared
emojiRegex.ts module and reuse the single instance.

Safe because every consumer resets lastIndex before use: String#match/#replace
do so internally, and hast-util-find-and-replace resets it before scanning. A
new test asserts that reset directly so a future library version cannot silently
break the shared instance.
messageCodeBlocks and matchMarkdownLinks created their RegExp literals on every
call. Hoist codeRegex, regexMdLinks, and singleMatch to module scope. Safe: the
first two are used only with String#match (resets lastIndex) and singleMatch is
non-global (.exec ignores lastIndex).
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

Size Change: +739 B (+0.11%)

Total Size: 652 kB

📦 View Changed
Filename Size Change
dist/cjs/emojis.js 2.54 kB +2 B (+0.08%)
dist/cjs/index.js 256 kB -4 B (0%)
dist/cjs/useNotificationApi.js 46.5 kB +355 B (+0.77%)
dist/es/emojis.mjs 2.47 kB -2 B (-0.08%)
dist/es/index.mjs 254 kB +33 B (+0.01%)
dist/es/useNotificationApi.mjs 45.3 kB +355 B (+0.79%)
ℹ️ View Unchanged
Filename Size
dist/cjs/audioProcessing.js 1.74 kB
dist/cjs/mp3-encoder.js 814 B
dist/css/emoji-picker.css 178 B
dist/css/emoji-replacement.css 456 B
dist/css/index.css 39.7 kB
dist/es/audioProcessing.mjs 1.65 kB
dist/es/mp3-encoder.mjs 768 B

compressed-size-action

@codecov

codecov Bot commented Jun 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.73%. Comparing base (4cddb02) to head (3cfd62e).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3202      +/-   ##
==========================================
+ Coverage   83.70%   83.73%   +0.03%     
==========================================
  Files         435      436       +1     
  Lines       13130    13131       +1     
  Branches     4252     4252              
==========================================
+ Hits        10990    10995       +5     
+ Misses       2140     2136       -4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@oliverlaz
oliverlaz merged commit 8c018a4 into master Jun 3, 2026
11 checks passed
@oliverlaz
oliverlaz deleted the perf/hoist-emoji-regex branch June 3, 2026 11:40
github-actions Bot pushed a commit that referenced this pull request Jun 5, 2026
## [14.4.0](v14.3.0...v14.4.0) (2026-06-05)

### Bug Fixes

* **compat:** restore React 17/18 compatibility in certain components ([#3197](#3197)) ([b513b69](b513b69))
* general performance and bug fixes ([#3201](#3201)) ([57c5795](57c5795))
* **interop:** unwrap CJS default exports (react-player, @emoji-mart/react) ([#3199](#3199)) ([4cddb02](4cddb02))
* maintain topmost modal non-inert ([#3206](#3206)) ([7ad98fa](7ad98fa))

### Features

* allow to stack modals on top of each other ([#3203](#3203)) ([4c934ae](4c934ae))
* display notifications above modals ([#3200](#3200)) ([0433090](0433090))
* **Reactions:** send emoji_code with reactions for push notification rendering ([#3209](#3209)) ([2faa620](2faa620))

### Performance Improvements

* **Message:** hoist regex compilation in message text rendering ([#3202](#3202)) ([8c018a4](8c018a4))
* **VideoPlayer:** lazy-load react-player to keep it out of the main bundle ([#3204](#3204)) ([18dc966](18dc966))
@stream-ci-bot

Copy link
Copy Markdown

🎉 This PR is included in version 14.4.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants