Skip to content

feat: support compile main-thread script to bytecode in external bundle#2459

Open
upupming wants to merge 2 commits intomainfrom
feat/external-bundle-mts-bytecode
Open

feat: support compile main-thread script to bytecode in external bundle#2459
upupming wants to merge 2 commits intomainfrom
feat/external-bundle-mts-bytecode

Conversation

@upupming
Copy link
Copy Markdown
Collaborator

@upupming upupming commented Apr 14, 2026

Summary by CodeRabbit

  • New Features

    • External bundles can now compile main-thread scripts to bytecode, improving startup performance and protection for main-thread code.
    • Configuration now accepts an option to target specific main-thread chunks for bytecode encoding.
  • Tests

    • Expanded tests to validate bytecode encoding and custom-section structure for main-thread bundle outputs.

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).
  • Changeset added, and when a BREAKING CHANGE occurs, it needs to be clearly marked (or not required).

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 14, 2026

🦋 Changeset detected

Latest commit: 5ce3b26

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

This PR includes changesets to release 3 packages
Name Type
@lynx-js/lynx-bundle-rslib-config Patch
@lynx-js/react-umd Patch
@lynx-js/react 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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 14, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 522316f7-629d-431a-92a2-bf9f87e8f84e

📥 Commits

Reviewing files that changed from the base of the PR and between 25f19ff and 5ce3b26.

📒 Files selected for processing (1)
  • packages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.ts

📝 Walkthrough

Walkthrough

Added support for compiling main-thread scripts to bytecode in external bundles: introduced a mainThreadChunks option, merged plugin wiring into a single external-bundle plugin that collects main-thread chunk names, updated encoding to mark matching JS assets as JsBytecode, and expanded tests to assert custom-section structures.

Changes

Cohort / File(s) Summary
Changeset & API
.​changeset/flat-bikes-boil.md, packages/rspeedy/lynx-bundle-rslib-config/etc/lynx-bundle-rslib-config.api.md
Added changeset; API report updated to include optional mainThreadChunks?: string[] on ExternalBundleWebpackPluginOptions.
Bundler wiring
packages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.ts
Merged prior plugins into one lynx:external-bundle plugin, changed signature to accept { engineVersion }, collected main-thread entry names (entryName + '.js') and pass mainThreadChunks to the webpack plugin; made api.modifyBundlerChain callback async.
Webpack plugin
packages/rspeedy/lynx-bundle-rslib-config/src/webpack/ExternalBundleWebpackPlugin.ts
Added mainThreadChunks?: string[] option; encode path adds customSections entry attribute 'encoding': 'JsBytecode' for assets matching mainThreadChunks; encode now receives useLepusNG: true.
Tests
packages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.ts
Strengthened assertions on decodedResult['custom-sections']: verify array/string shapes and numeric markers for index:CSS, index, and index__main-thread entries indicating expected bytecode sections.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • colinaaa
  • luhc228

Poem

🐰 Hop-hop, I scurry through the build,
I count main-thread chunks and keep them thrilled,
I whisper "JsBytecode" in each matched file,
Bundles sleep light and wake with a smile. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding support for compiling main-thread scripts to bytecode in external bundles, which is the primary objective reflected across all modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/external-bundle-mts-bytecode

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 14, 2026

❌ 10 Tests Failed:

Tests completed Failed Passed Skipped
4784 10 4774 98
View the full list of 10 ❄️ flaky test(s)
packages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.ts > NODE_ENV configuration > should output different artifacts for development and production NODE_ENV

Flake rate in main: 8.33% (Passed 99 times, Failed 9 times)

Stack Traces | 2.15s run time
AssertionError: expected undefined not to be undefined // Object.is equality
 ❯ test/external-bundle.test.ts:335:31
packages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.ts > mount externals library > should apply reactlynx externals preset to the final bundle

Flake rate in main: 9.09% (Passed 40 times, Failed 4 times)

Stack Traces | 0.373s run time
AssertionError: expected [ 'utils' ] to deeply equal [ 'utils', 'utils__main-thread' ]

- Expected
+ Received

  [
    "utils",
-   "utils__main-thread",
  ]

 ❯ test/external-bundle.test.ts:474:66
packages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.ts > mount externals library > should let explicit externals override the reactlynx preset

Flake rate in main: 9.09% (Passed 40 times, Failed 4 times)

Stack Traces | 0.365s run time
AssertionError: the given combination of arguments (undefined and string) is invalid for this assertion. You can use an array, a map, an object, a set, a string, or a weakset instead of a string
 ❯ test/external-bundle.test.ts:521:68
packages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.ts > mount externals library > should mount externals library to globalThis

Flake rate in main: 8.33% (Passed 99 times, Failed 9 times)

Stack Traces | 0.434s run time
AssertionError: expected [ 'utils' ] to deeply equal [ 'utils', 'utils__main-thread' ]

- Expected
+ Received

  [
    "utils",
-   "utils__main-thread",
  ]

 ❯ test/external-bundle.test.ts:646:66
packages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.ts > mount externals library > should mount externals library to lynx by default

Flake rate in main: 8.33% (Passed 99 times, Failed 9 times)

Stack Traces | 0.329s run time
AssertionError: expected [ 'utils' ] to deeply equal [ 'utils', 'utils__main-thread' ]

- Expected
+ Received

  [
    "utils",
-   "utils__main-thread",
  ]

 ❯ test/external-bundle.test.ts:420:66
packages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.ts > pluginReactLynx > should handle macros

Flake rate in main: 8.33% (Passed 99 times, Failed 9 times)

Stack Traces | 0.000736s run time
AssertionError: expected [ 'utils' ] to deeply equal [ 'utils', 'utils__main-thread' ]

- Expected
+ Received

  [
    "utils",
-   "utils__main-thread",
  ]

 ❯ test/external-bundle.test.ts:741:66
packages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.ts > should build external bundle > should build both main-thread and background code into external bundle

Flake rate in main: 8.33% (Passed 99 times, Failed 9 times)

Stack Traces | 1.81s run time
AssertionError: expected [ 'utils' ] to deeply equal [ 'utils', 'utils__main-thread' ]

- Expected
+ Received

  [
    "utils",
-   "utils__main-thread",
  ]

 ❯ test/external-bundle.test.ts:123:66
packages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.ts > should build external bundle > should build css into external bundle

Flake rate in main: 8.33% (Passed 99 times, Failed 9 times)

Stack Traces | 0.451s run time
AssertionError: expected [ 'index', 'index:CSS' ] to deeply equal [ 'index', 'index:CSS', …(1) ]

- Expected
+ Received

  [
    "index",
    "index:CSS",
-   "index__main-thread",
  ]

 ❯ test/external-bundle.test.ts:241:66
packages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.ts > should build external bundle > should include LoadingConsumerModulesRuntimeModule in the main-thread bundle

Flake rate in main: 8.33% (Passed 99 times, Failed 9 times)

Stack Traces | 1.55s run time
AssertionError: the given combination of arguments (undefined and string) is invalid for this assertion. You can use an array, a map, an object, a set, a string, or a weakset instead of a string
 ❯ test/external-bundle.test.ts:289:68
packages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.ts > should build external bundle > should only build main-thread code into external bundle

Flake rate in main: 8.33% (Passed 99 times, Failed 9 times)

Stack Traces | 0.71s run time
AssertionError: expected [] to deeply equal [ 'utils' ]

- Expected
+ Received

- [
-   "utils",
- ]
+ []

 ❯ test/external-bundle.test.ts:153:59

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/rspeedy/lynx-bundle-rslib-config/src/webpack/ExternalBundleWebpackPlugin.ts`:
- Around line 138-142: The check in ExternalBundleWebpackPlugin that uses
this.options.mainThreadChunks?.includes(cur.name) compares against the emitted
asset filename (cur.name) which can include hashes/dirs; change it to compare
normalized chunk/section identity: derive a stable name (e.g., use
cur.chunk?.name or extract a basename/normalized section name from cur.name) and
compare that against a normalized version of this.options.mainThreadChunks (map
the configured names through the same normalization). Update the condition to
use the normalizedChunkName (instead of cur.name) so the JsBytecode branch is
chosen by chunk identity, not the raw emitted filename.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4c307d79-ebf4-4bb5-932b-8792c7227ca4

📥 Commits

Reviewing files that changed from the base of the PR and between 7332eb4 and 25f19ff.

📒 Files selected for processing (5)
  • .changeset/flat-bikes-boil.md
  • packages/rspeedy/lynx-bundle-rslib-config/etc/lynx-bundle-rslib-config.api.md
  • packages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.ts
  • packages/rspeedy/lynx-bundle-rslib-config/src/webpack/ExternalBundleWebpackPlugin.ts
  • packages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.ts

Comment on lines +138 to +142
...(this.options.mainThreadChunks?.includes(cur.name)
? {
'encoding': 'JsBytecode',
}
: {}),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Match main-thread assets by chunk identity, not exact emitted filename.

mainThreadChunks?.includes(cur.name) only works when the emitted asset name is exactly the stored value. As soon as a caller uses a subdirectory or hash in the JS filename template, this branch stops tagging the main-thread section as JsBytecode and silently falls back to plain JS. Compare against a normalized section/chunk name instead of the raw emitted filename.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/rspeedy/lynx-bundle-rslib-config/src/webpack/ExternalBundleWebpackPlugin.ts`
around lines 138 - 142, The check in ExternalBundleWebpackPlugin that uses
this.options.mainThreadChunks?.includes(cur.name) compares against the emitted
asset filename (cur.name) which can include hashes/dirs; change it to compare
normalized chunk/section identity: derive a stable name (e.g., use
cur.chunk?.name or extract a basename/normalized section name from cur.name) and
compare that against a normalized version of this.options.mainThreadChunks (map
the configured names through the same normalization). Update the condition to
use the normalizedChunkName (instead of cur.name) so the JsBytecode branch is
chosen by chunk identity, not the raw emitted filename.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Apr 14, 2026

Merging this PR will improve performance by 11.24%

⚡ 1 improved benchmark
✅ 80 untouched benchmarks
⏩ 26 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
transform 1000 view elements 45.4 ms 40.8 ms +11.24%

Comparing feat/external-bundle-mts-bytecode (5ce3b26) with main (95c2dc3)

Open in CodSpeed

Footnotes

  1. 26 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@relativeci
Copy link
Copy Markdown

relativeci bot commented Apr 14, 2026

React Example

#7383 Bundle Size — 223.33KiB (0%).

5ce3b26(current) vs 95c2dc3 main#7382(baseline)

Bundle metrics  no changes
                 Current
#7383
     Baseline
#7382
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 4 4
No change  Modules 179 179
No change  Duplicate Modules 69 69
No change  Duplicate Code 44.48% 44.48%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#7383
     Baseline
#7382
No change  IMG 145.76KiB 145.76KiB
No change  Other 77.58KiB 77.58KiB

Bundle analysis reportBranch feat/external-bundle-mts-bytecod...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci bot commented Apr 14, 2026

Web Explorer

#8957 Bundle Size — 749.55KiB (0%).

5ce3b26(current) vs 95c2dc3 main#8956(baseline)

Bundle metrics  Change 2 changes
                 Current
#8957
     Baseline
#8956
No change  Initial JS 44.45KiB 44.45KiB
No change  Initial CSS 2.16KiB 2.16KiB
No change  Cache Invalidation 0% 0%
No change  Chunks 8 8
No change  Assets 10 10
Change  Modules 148(-0.67%) 149
No change  Duplicate Modules 11 11
Change  Duplicate Code 34.99%(+0.03%) 34.98%
No change  Packages 3 3
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#8957
     Baseline
#8956
No change  Other 401.63KiB 401.63KiB
No change  JS 345.76KiB 345.76KiB
No change  CSS 2.16KiB 2.16KiB

Bundle analysis reportBranch feat/external-bundle-mts-bytecod...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci bot commented Apr 14, 2026

React External

#500 Bundle Size — 670.93KiB (+15.61%).

5ce3b26(current) vs 95c2dc3 main#499(baseline)

Bundle metrics  Change 1 change
                 Current
#500
     Baseline
#499
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
Change  Cache Invalidation 98.34% 0%
No change  Chunks 0 0
No change  Assets 3 3
No change  Modules 17 17
No change  Duplicate Modules 5 5
No change  Duplicate Code 8.59% 8.59%
No change  Packages 0 0
No change  Duplicate Packages 0 0
Bundle size by type  Change 1 change Regression 1 regression
                 Current
#500
     Baseline
#499
Regression  Other 670.93KiB (+15.61%) 580.35KiB

Bundle analysis reportBranch feat/external-bundle-mts-bytecod...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci bot commented Apr 14, 2026

React MTF Example

#516 Bundle Size — 193.94KiB (0%).

5ce3b26(current) vs 95c2dc3 main#515(baseline)

Bundle metrics  no changes
                 Current
#516
     Baseline
#515
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 3 3
No change  Modules 173 173
No change  Duplicate Modules 66 66
No change  Duplicate Code 43.94% 43.94%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#516
     Baseline
#515
No change  IMG 111.23KiB 111.23KiB
No change  Other 82.71KiB 82.71KiB

Bundle analysis reportBranch feat/external-bundle-mts-bytecod...Project dashboard


Generated by RelativeCIDocumentationReport issue

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