Visitar URL original
refactor: move the debug metadata plugin into @lynx-js/rsbuild-plugin by upupming · Pull Request #3596 · lynx-family/lynx-stack · GitHub
Skip to content

refactor: move the debug metadata plugin into @lynx-js/rsbuild-plugin - #3596

Merged
upupming merged 1 commit into
feat/auto-load-plugin-lynxfrom
feat/engine-debug-metadata
Aug 20, 2026
Merged

upupming merged 1 commit into
feat/auto-load-plugin-lynxfrom
feat/engine-debug-metadata

Conversation

@upupming

@upupming upupming commented Aug 19, 2026 •

Copy link
Copy Markdown
Collaborator

Part of the series that moves the Lynx build engine out of @lynx-js/rspeedy and into pluginLynx.

pluginLynxDebugMetadata was the last engine plugin still applied from Rspeedy's own default plugin list. Applying it from pluginLynx gives an Rsbuild build the same debug metadata as an Rspeedy build.

Stacked on #3578.

Summary by CodeRabbit

  • Bug Fixes

    • Debug metadata support is now included through the Lynx plugin.
    • Existing Rspeedy configurations continue to receive debug metadata without requiring separate setup.
  • Chores

    • Updated package release metadata to reflect the change.

@changeset-bot

changeset-bot Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a323392

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

This PR includes changesets to release 6 packages
Name Type
@lynx-js/rsbuild-plugin Patch
@lynx-js/rspeedy Patch
@lynx-js/react-rsbuild-plugin Patch
upgrade-rspeedy Patch
@lynx-js/react-alias-rsbuild-plugin Patch
create-rspeedy 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

coderabbitai Bot commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1948e918-1915-4ab6-9e33-856b25cbab09

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 53add258-f02d-41b7-b4e8-2e00ed05429e

📥 Commits

Reviewing files that changed from the base of the PR and between 1664f5e and bf51753.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • .changeset/engine-debug-metadata.md
  • packages/rspeedy/core/package.json
  • packages/rspeedy/core/src/plugins/index.ts
  • packages/rspeedy/plugin-lynx/package.json
  • packages/rspeedy/plugin-lynx/src/index.ts
💤 Files with no reviewable changes (2)
  • packages/rspeedy/core/src/plugins/index.ts
  • packages/rspeedy/core/package.json

Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The debug metadata plugin moves from Rspeedy core into pluginLynx. Its dependency and registration move with it, and a changeset records patch releases for the affected packages.

Changes

Debug metadata plugin relocation

Layer / File(s) Summary
Move dependency and registration
packages/rspeedy/core/package.json, packages/rspeedy/core/src/plugins/index.ts, packages/rspeedy/plugin-lynx/package.json, packages/rspeedy/plugin-lynx/src/index.ts
Rspeedy core removes the debug metadata dependency and default registration. plugin-lynx adds the dependency and registers pluginLynxDebugMetadata() through pluginLynx().
Record package releases
.changeset/engine-debug-metadata.md
The changeset records patch releases for @lynx-js/rsbuild-plugin and @lynx-js/rspeedy.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to bf517

This PR moves debug metadata plugin application into the Lynx plugin package without any supplied evidence of a merge-blocking issue; it is merge-ready after normal checks and review.

Possibly related PRs

Suggested reviewers: colinaaa, luhc228

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes moving the debug metadata plugin into @lynx-js/rsbuild-plugin.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/engine-debug-metadata

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.

@pkg-pr-new

pkg-pr-new Bot commented Aug 19, 2026 •

Copy link
Copy Markdown
background-only

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/background-only@a323392

@lynx-js/use-sync-external-store

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/use-sync-external-store@a323392

@lynx-js/rspeedy

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/rspeedy@a323392

create-rspeedy

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/create-rspeedy@a323392

@lynx-js/debug-metadata-rsbuild-plugin

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/debug-metadata-rsbuild-plugin@a323392

@lynx-js/rsbuild-plugin

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/rsbuild-plugin@a323392

@lynx-js/react-rsbuild-plugin

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/react-rsbuild-plugin@a323392

@lynx-js/react-alias-rsbuild-plugin

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/react-alias-rsbuild-plugin@a323392

upgrade-rspeedy

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/upgrade-rspeedy@a323392

@lynx-js/websocket

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/websocket@a323392

@lynx-js/css-serializer

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/css-serializer@a323392

@lynx-js/debug-metadata

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/debug-metadata@a323392

@lynx-js/web-core

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/web-core@a323392

@lynx-js/web-elements

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/web-elements@a323392

@lynx-js/web-rsbuild-server-middleware

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/web-rsbuild-server-middleware@a323392

@lynx-js/web-worker-rpc

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/web-worker-rpc@a323392

@lynx-js/cache-events-webpack-plugin

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/cache-events-webpack-plugin@a323392

@lynx-js/chunk-loading-webpack-plugin

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/chunk-loading-webpack-plugin@a323392

@lynx-js/css-extract-webpack-plugin

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/css-extract-webpack-plugin@a323392

@lynx-js/react-refresh-webpack-plugin

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/react-refresh-webpack-plugin@a323392

@lynx-js/react-webpack-plugin

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/react-webpack-plugin@a323392

@lynx-js/runtime-wrapper-webpack-plugin

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/runtime-wrapper-webpack-plugin@a323392

@lynx-js/template-webpack-plugin

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/template-webpack-plugin@a323392

@lynx-js/webpack-dev-transport

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/webpack-dev-transport@a323392

@lynx-js/webpack-runtime-globals

pnpm add https://pkg.pr.new/lynx-family/lynx-stack/@lynx-js/webpack-runtime-globals@a323392

commit: a323392

@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@codspeed

codspeed Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

Merging this PR will degrade performance by 7.44%

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

❌ 1 regressed benchmark
✅ 267 untouched benchmarks
⏩ 26 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ WallTime basic-performance-div-1000 7.4 ms 8 ms -7.44%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing feat/engine-debug-metadata (a323392) with feat/auto-load-plugin-lynx (dc6c022)

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

relativeci Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

React External

#4338 Bundle Size — 581.87KiB (0%).

a323392(current) vs dc6c022 feat/auto-load-plugin-lynx#4318(baseline)

Bundle metrics  Change 1 change
                 Current
#4338
     Baseline
#4318
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 20 20
No change  Duplicate Modules 5 5
Change  Duplicate Code 5.57%(+2.39%) 5.44%
No change  Packages 0 0
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#4338
     Baseline
#4318
No change  Other 581.87KiB 581.87KiB

Bundle analysis report Branch feat/engine-debug-metadata Project dashboard


Generated by RelativeCI Documentation Report issue

@relativeci

relativeci Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

React MTF Example

#4356 Bundle Size — 210.21KiB (0%).

a323392(current) vs dc6c022 feat/auto-load-plugin-lynx#4335(baseline)

Bundle metrics  no changes
                 Current
#4356
     Baseline
#4335
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 214 214
No change  Duplicate Modules 81 81
No change  Duplicate Code 43% 43%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#4356
     Baseline
#4335
No change  IMG 111.23KiB 111.23KiB
No change  Other 98.98KiB 98.98KiB

Bundle analysis report Branch feat/engine-debug-metadata Project dashboard


Generated by RelativeCI Documentation Report issue

@relativeci

relativeci Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

React Example with Element Template

#3501 Bundle Size — 225.42KiB (0%).

a323392(current) vs dc6c022 feat/auto-load-plugin-lynx#3479(baseline)

Bundle metrics  no changes
                 Current
#3501
     Baseline
#3479
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 160 160
No change  Duplicate Modules 56 56
No change  Duplicate Code 36.73% 36.73%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#3501
     Baseline
#3479
No change  IMG 145.76KiB 145.76KiB
No change  Other 79.67KiB 79.67KiB

Bundle analysis report Branch feat/engine-debug-metadata Project dashboard


Generated by RelativeCI Documentation Report issue

@relativeci

relativeci Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

React Example

#11238 Bundle Size — 238.98KiB (0%).

a323392(current) vs dc6c022 feat/auto-load-plugin-lynx#11217(baseline)

Bundle metrics  no changes
                 Current
#11238
     Baseline
#11217
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 219 219
No change  Duplicate Modules 84 84
No change  Duplicate Code 43.56% 43.56%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#11238
     Baseline
#11217
No change  IMG 145.76KiB 145.76KiB
No change  Other 93.22KiB 93.22KiB

Bundle analysis report Branch feat/engine-debug-metadata Project dashboard


Generated by RelativeCI Documentation Report issue

@relativeci

relativeci Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

Web Explorer

#12819 Bundle Size — 1.29MiB (0%).

a323392(current) vs dc6c022 feat/auto-load-plugin-lynx#12800(baseline)

Bundle metrics  Change 1 change
                 Current
#12819
     Baseline
#12800
No change  Initial JS 45.99KiB 45.99KiB
No change  Initial CSS 2.37KiB 2.37KiB
No change  Cache Invalidation 0% 0%
No change  Chunks 11 11
No change  Assets 13 13
Change  Modules 322(+0.31%) 321
No change  Duplicate Modules 15 15
No change  Duplicate Code 19.35% 19.35%
No change  Packages 14 14
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#12819
     Baseline
#12800
No change  JS 757.55KiB 757.55KiB
No change  Other 564.93KiB 564.93KiB
No change  CSS 2.37KiB 2.37KiB

Bundle analysis report Branch feat/engine-debug-metadata Project dashboard


Generated by RelativeCI Documentation Report issue

@upupming

Copy link
Copy Markdown
Collaborator Author

The CodSpeed check is measurement noise, not a regression from this PR. It flags basic-performance-div-100 in packages/web-platform/web-core-e2e at 1.4 ms -> 1.5 ms, measured with the WallTime instrument on a standard hosted runner, which CodSpeed's own report warns gives inconsistent data. This PR only moves where a plugin is applied and does not touch the web platform. The PR stacked on top of this one flags a different benchmark in the same suite while only adding a test file. Done passes on both.

@upupming

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 20, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@upupming
upupming force-pushed the feat/engine-debug-metadata branch 2 times, most recently from e2c1861 to 8f51a56 Compare August 20, 2026 03:27
@upupming
upupming force-pushed the feat/engine-debug-metadata branch from 8f51a56 to fe62ab4 Compare August 20, 2026 05:06
luhc228
luhc228 previously approved these changes Aug 20, 2026
stack merge was automatically disabled August 20, 2026 08:04

Pull Request is not mergeable

@luhc228
luhc228 force-pushed the feat/engine-debug-metadata branch from fe62ab4 to a323392 Compare August 20, 2026 08:05
@upupming
upupming merged commit 99ed745 into main Aug 20, 2026
140 of 152 checks passed
@upupming
upupming deleted the feat/engine-debug-metadata branch August 20, 2026 11:14
upupming added a commit that referenced this pull request Aug 20, 2026
Part of the series that moves the Lynx build engine out of
`@lynx-js/rspeedy` and into `pluginLynx`.

The point of the series is that an Rsbuild build with only
`pluginReactLynx()` produces the same bundle as an Rspeedy build. This
locks that in: the same fixture is built both ways, each in its own
process, in production and in development, and the two bundles are
compared.

The comparison ignores identifiers minted per build rather than derived
from the source. `chunk.hash` is not reproducible across builds, and
both the content hash in a filename and the debug metadata release are
keyed on it, so the filename hash is turned off and the release is
stripped.

That reproducibility problem is not ours: swc's `inline_globals` keeps
the span of the value it substitutes for a define, and those values come
from a process-wide cache, so their spans belong to a `SourceMap` from
an earlier call. The module's source map therefore moves between builds,
which moves `build_info.hash`, `chunk.hash` and the content hash. Fix
proposed upstream in swc-project/swc#12129; with it, 40 Rspeedy builds
of the same source produce one bundle instead of ten. Once that lands
the two allowances in this test can go away.

Stacked on #3596.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Tests**
* Added coverage to verify that development and production bundles
generated by Rsbuild and Rspeedy remain equivalent.
* Normalized build-specific metadata during comparisons for reliable
results.
  * Isolated builds and cleaned up temporary output after testing.

* **Chores**
* Added a parity build utility using consistent React, environment,
output, and deterministic build settings.
  * Documented the parity coverage in the upcoming patch release notes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
upupming pushed a commit that referenced this pull request Aug 21, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @lynx-js/react@0.125.0

### Minor Changes

- Add the `<background-only>` element, which renders its `fallback` on
the main thread first screen and its children on the background thread.
([#3551](#3551))

### Patch Changes

- Tag main-thread vnodes with `$$typeof: Symbol.for('react.element')` so
`isValidElement` recognizes them, matching the background thread.
([#3514](#3514))

- Release worklet refs when list items are logically removed while
preserving native element recycling.
([#3590](#3590))
## @lynx-js/web-core@0.25.0

### Minor Changes

- Support `lynx.createIntersectionObserver` in Lynx for Web.
([#3383](#3383))

- Load a hand-written Lynx XML markup card in the browser, by compiling
it into a ([#3404](#3404))
  `.web.bundle` there and then loading that.

A markup card is not a kind of artifact. The decode worker already
dispatches on
the eight header bytes it reads - `{` for a `.json` template, the magic
header for
a bundle - and a markup card is what neither of those claims, so it
costs the two
shapes that stream nothing to reach. From there it is handed to
`encodeLynxXML`,
the same function `@lynx-js/web-core/encode` gives a build, which
returns real
bundle bytes: magic header, version, and the five sections in the
encoder's order.
Those bytes go straight back to `handleStream`, so every section is read
by the
reader that already existed. There is no markup decoding anywhere - not
in the
worker, not on the main thread - and a markup card is not merely
equivalent to a
  built card, it _is_ one by the time anything decodes it.

Compiling in the browser is what #3589 made possible: `binary/encode`'s
glue used
  to load its wasm through `node:fs`, and is now generated with
`wasm-bindgen --target bundler`, which a bundler resolves on either
platform.

  **What this costs, measured**

Both sides rebuilt from source - `npm run build:wasm` then `rsbuild
build`, with
`--force` so neither figure is a cache replay - because `binary/` is
gitignored
and survives `git switch`, which has produced wrong numbers here before.
Raw /
  `gzip -9`.

| artifact | `origin/main` | this change | delta |
| ----------------------------------- | ---------------- |
---------------- | ------------- |
| `binary/client/client_bg.wasm` | 227,536 / 82,883 | _byte-identical_ |
0 |
| `binary/client_legacy/…_bg.wasm` | 183,444 / 74,949 | _byte-identical_
| 0 |
| eager `client.js` | 45,287 / 14,388 | _byte-identical_ | 0 |
| `web-core-main-chunk.js` | 159,621 / 33,074 | _byte-identical_ | 0 |
| `web-core-worker-chunk.js` | 15,135 / 6,001 | _byte-identical_ | 0 |
| worker chunk (`…-loader-thread.js`) | 33,257 / 9,912 | 34,755 / 10,357
| +1,498 / +445 |
| `web-core-markup-encoder.js` (new) | – | 227,811 / 64,809 | new, lazy
|
| encode wasm asset (new) | – | 167,689 / 55,689 | new, lazy |

So a card that was built ahead of time pays **+1,498 B raw / +445 B
gzip**, all of
it in the worker chunk, and nothing at all in the eager entry or the
main chunk.
The four byte-identical rows are sha256 comparisons, not size
comparisons.

The laziness is load bearing rather than tidy: `TemplateManager`
requests the
worker with `webpackPrefetch`, `webpackPreload` and `fetchPriority:
"high"`, so a
static import would eagerly fetch all 395 kB for _every_ card. Verified
positively
and with a negative control on `origin/main`, counting occurrences (a
minified
  chunk is one line, so a line count cannot tell 1 from 60):

| marker | eager `client.js` | worker chunk | main chunk | markup chunk
|
| ---------------------------- | ----------------- | ------------ |
---------- | ------------ |
| encode wasm asset name | 0 | 0 | 0 | 1 |
| `css-tree` token names | 0 / 0 | 0 / 0 | 0 / 0 | 2 / 2 |
| the XML parser's own message | 0 | 0 | 0 | 2 |

All of these are 0 everywhere on `origin/main`, including in the chunk
that does
not exist there. `encode_legacy_json_generated_raw_style_info` reads 2
in the
eager entry and 3 in the worker chunk on **both** sides - it is the
_client_ wasm's
own export, used by `cssLoader` for `.json` artifacts, and is not this
change.

Compiling itself is work that moved from a build into the browser,
medians of 41
interleaved rounds: a 72 B stylesheet takes 0.42 ms, 872 B takes 0.89
ms, 9.1 kB
  takes 7.4 ms and 26 kB takes 23.5 ms. Only markup cards pay it.

  **Reviewer decisions this change deliberately leaves open**

- **`encodeLynxXML` warns on the console unconditionally, and now does
so at
runtime.** It reports each at-rule the Lynx style format cannot carry.
That was
written when the only caller was a build, which has no production
runtime to
stay quiet for; the same code now runs in a browser. It is left exactly
as it is
on `origin/main` so that `ts/encode/` keeps a zero diff, but gating it
on a dev
build, or deduplicating it per at-rule name, are both reasonable and
neither is
    done here.
- **The published tarball grows by 397 kB**, being the new markup chunk
plus the
encode wasm, which rspack now also emits under
`dist/client_prod/static/wasm/`.
That wasm is consequently present three times in the package - there,
under
`dist/encode_prod/static/wasm/` since #3589, and under `binary/encode/`.
Nothing
here makes that worse than the pattern already in place for the client
wasm, and
reclaiming it is a `files` change that would alter what deep importers
can
    reach, so it is left out of this change.
- **If #3390 lands first, the two chunk figures above need
re-measuring.** It
    reaches `css-tree` directly where this change reaches it through
`@lynx-js/css-serializer`; the spec resolves to a single
`css-tree@3.2.1`, so
rspack would either duplicate it into both lazy chunks or hoist it into
a shared
one. Nobody has built the union yet, so no combined figure is quoted
here.

  **Other limits worth knowing**

- `@lynx-js/css-serializer` becomes a real `dependency` rather than the
optional
peer it was. `dist/client` ships as unbundled ESM, so the `import` the
compiler
chunk performs is resolved by the consumer, and an optional peer nobody
installs
would make a markup card fail to load in exactly the packaging that
looks fine
    on disk.
- **A corrupted bundle now reaches the markup path**, because markup is
what is
left when a response is neither a bundle nor JSON. Handing those bytes
to the XML
parser would answer `expected '<lynx version="...">' root element`,
which points
the reader at a markup bug in a file that is not markup, so the two are
told
apart first - before the compiler chunk is even fetched - on whether the
content
begins a tag at all. Bytes that do not keep the diagnosis they always
had,
`Invalid Magic Header`, now carrying the eight header bytes that failed
to match;
a document that does gets the XML parser's own message and offset. A
response
shorter than 8 bytes is still rejected by the header read, exactly as
before.
- `@media`, `@supports` and `@layer` are dropped, as they already were
when
building a markup card into a bundle: Lynx's style format has no rule
kind for a
conditional group, so they are not Lynx features on any platform. Same
for
    `@import` with a URL.
- The `handleMarkup` recursion is one level deep and cannot loop:
`encode` writes
the magic header at offset 0 unconditionally, so the second
`handleStream` takes
the binary branch. Were that untrue, the bytes would fail the "begins a
tag"
check and the recursion would end in a thrown error rather than a cycle.

### Patch Changes

- Build `binary/encode` with `wasm-bindgen --target bundler` and publish
`@lynx-js/web-core/encode` as an rslib bundle, so one set of artifacts
serves both Node and the browser.
([#3589](#3589))

  **Why**

`binary/encode` used to be generated with `--target
experimental-nodejs-module`, whose glue calls `readFileSync('node:fs')`
at module scope. That made `@lynx-js/web-core/encode` importable from
Node only. The `bundler` target instead emits an "async wasm module"
glue (`import * as wasm from './encode_bg.wasm'`), which a bundler can
resolve for either platform.

  **What changed in the published package**

- `./encode` now resolves to `dist/encode_prod/index.js` (bundled by
rslib, like `./server` already was) instead of the unbundled
`dist/encode/index.js` emitted by `tsc`. The wasm is emitted as a build
asset under `dist/encode_prod/static/wasm/`.
- Bundling is what keeps the import clean: consuming the `bundler` glue
directly from Node would work only on Node 22 or newer, and would print
`ExperimentalWarning: Importing WebAssembly module instances` on every
build. Letting rslib resolve the wasm at web-core build time avoids
both.
- `encode_bg.wasm` is byte-for-byte unchanged; only the JavaScript glue
differs. The `binary/encode/*.d.ts` type declarations are identical
under both targets.

  **Compatibility**

- No API change. `encode()` and `encodeCSS(cssMap): Uint8Array` keep
their synchronous signatures. The wasm initialization becomes a single
module-level `await` inside the bundle, which is already accommodated by
the `await import('@lynx-js/web-core/encode')` that consumers use.
Encoded output is byte-identical to the previous release.
- `@lynx-js/css-serializer` stays an external dependency of the bundle
and is not inlined.
- Note on Node versions: `@lynx-js/web-core/encode` does not work on
Node 20 and did not work there before this change either. The `encode`
wasm is optimized with `wasm-opt --all-features`, and Node 20's engine
rejects it with `CompileError: Unknown heap type -14` regardless of
which glue is used. This is a pre-existing limitation that this change
neither introduces nor fixes; the effective floor for this entry point
is Node 22.
- The tarball grows by roughly the size of the encode wasm, because
`binary/encode/encode_bg.wasm` is still shipped alongside the copy that
rslib emits into `dist/encode_prod/`.
- Prevent event dispatch failures from escaping through the WebAssembly
boundary. ([#3356](#3356))
- Updated dependencies
[[`9c2be3e`](9c2be3e),
[`d671851`](d671851),
[`d671851`](d671851)]:
  - @lynx-js/css-serializer@0.1.9
  - @lynx-js/web-elements@0.12.9
  - @lynx-js/web-worker-rpc@0.25.0
## @lynx-js/genui@0.3.1

### Patch Changes

- Make A2UI image prompt guidance provider-neutral so host agents can
generate ([#3537](#3537))
  image assets through their configured tools.
- Updated dependencies
[[`d8f80cd`](d8f80cd)]:
  - @lynx-js/react-signals@0.0.2
## @lynx-js/react-signals@0.0.2

### Patch Changes

- Widen the `@lynx-js/react` peer dependency range to include 0.125.
([#3551](#3551))
## @lynx-js/rspeedy@0.16.5

### Patch Changes

- Move the debug metadata plugin from `@lynx-js/rspeedy` into
`pluginLynx`.
([#3596](#3596))

- Move `pluginDev` into `pluginLynx()`.
([#3364](#3364))

- Move the cssnano-based CSS minimizer into `pluginLynx()`.
([#3364](#3364))

- Move the `tools.htmlPlugin` default into `pluginLynx()`.
([#3364](#3364))

- Move the `output.legalComments` default into `pluginLynx()`.
([#3364](#3364))

- Read the bundle filename from the Rsbuild config instead of the
Rspeedy API.
([#3570](#3570))
- Updated dependencies
[[`99ed745`](99ed745),
[`0d10b79`](0d10b79),
[`0d10b79`](0d10b79),
[`0d10b79`](0d10b79),
[`0d10b79`](0d10b79),
[`92b54ed`](92b54ed),
[`37497d5`](37497d5)]:
  - @lynx-js/rsbuild-plugin@0.0.3
## @lynx-js/lynx-bundle-rslib-config@0.7.1

### Patch Changes

- Updated dependencies
[[`9c2be3e`](9c2be3e),
[`cf98e14`](cf98e14),
[`3bf135c`](3bf135c),
[`ae25e93`](ae25e93),
[`9c2be3e`](9c2be3e)]:
  - @lynx-js/css-serializer@0.1.9
  - @lynx-js/web-core@0.25.0
## @lynx-js/rsbuild-plugin@0.0.3

### Patch Changes

- Move the debug metadata plugin from `@lynx-js/rspeedy` into
`pluginLynx`.
([#3596](#3596))

- Move `pluginDev` into `pluginLynx()`.
([#3364](#3364))

- Move the cssnano-based CSS minimizer into `pluginLynx()`.
([#3364](#3364))

- Move the `tools.htmlPlugin` default into `pluginLynx()`.
([#3364](#3364))

- Move the `output.legalComments` default into `pluginLynx()`.
([#3364](#3364))

- Add `PLUGIN_LYNX_NAME` so plugins can tell whether `pluginLynx` is
applied. ([#3576](#3576))

- Alias `@rspack/core/hot/log.js` and
`@rspack/core/hot/log-apply-result.js`, which
`@lynx-js/webpack-dev-transport` imports without declaring
`@rspack/core`. Development builds failed to resolve them on any package
manager that does not hoist `@rspack/core`, such as npm and Yarn.
([#3577](#3577))
- Updated dependencies []:
  - @lynx-js/web-rsbuild-server-middleware@0.25.0
## @lynx-js/react-rsbuild-plugin@0.19.1

### Patch Changes

- Apply the Lynx build engine automatically when it is not already
applied, so `pluginReactLynx` works with plain Rsbuild.
([#3578](#3578))

- Read the bundle filename from the Rsbuild config instead of the
Rspeedy API.
([#3570](#3570))

- Emit the Lynx template for callers other than `rslib` and `rstest`.
([#3572](#3572))

- Widen the `@lynx-js/react` peer dependency range to include 0.125.
([#3551](#3551))
- Updated dependencies
[[`1d08862`](1d08862),
[`99ed745`](99ed745),
[`0d10b79`](0d10b79),
[`0d10b79`](0d10b79),
[`0d10b79`](0d10b79),
[`0d10b79`](0d10b79),
[`92b54ed`](92b54ed),
[`37497d5`](37497d5)]:
  - @lynx-js/react-webpack-plugin@0.11.1
  - @lynx-js/rsbuild-plugin@0.0.3
  - @lynx-js/react-alias-rsbuild-plugin@0.19.1
  - @lynx-js/use-sync-external-store@1.5.0
  - @lynx-js/react-refresh-webpack-plugin@0.4.2
  - @lynx-js/css-extract-webpack-plugin@0.10.1
  - @lynx-js/template-webpack-plugin@0.15.2
## @lynx-js/vanilla-rsbuild-plugin@0.0.2

### Patch Changes

- Updated dependencies []:
  - @lynx-js/template-webpack-plugin@0.15.2
## @lynx-js/css-serializer@0.1.9

### Patch Changes

- Point `@lynx-js/source-field` at `src/index.ts`, so a bundler in this
repo can ([#3404](#3404))
  resolve the package without waiting for its `dist/`.

`@lynx-js/css-serializer` has no `build` script - its `dist/` is emitted
by the
repository's root `tsc --build`, which is a separate turbo task with no
ordering
relationship to any package's `build`. That was invisible while every
consumer
was itself type-checked rather than bundled, but `@lynx-js/web-core`
bundles its
client with rspack, and rspack resolving `main: dist/index.js` before
the root
  build has emitted it fails outright.

`@rsbuild/plugin-source-build` is already how this repository answers
that -
`@lynx-js/web-elements` and `@lynx-js/web-worker-rpc` declare the same
field and
are consumed straight from source. Declaring it here removes the
ordering
  question rather than scheduling around it.

The field is inert outside such a build: it is a plain, unknown
top-level
`package.json` key, so no `exports` map is introduced and no existing
entry point
  or deep import changes.
## @lynx-js/web-elements@0.12.9

### Patch Changes

- Clamp the `setFoldExpanded` offset of `<x-foldview-ng>` to the
scrollable length.
([#3290](#3290))

`setFoldExpanded` called the native `scrollTo`, bypassing the clamping
done by the
`scrollTop` setter. A page collapsing its header with a deliberately
large offset
(e.g. `offset: '99999px'`) scrolled past the end, which does not happen
on native.
- Give `<x-foldview-header-ng>` a `width: 100%`.
([#3290](#3290))

The element is laid out with `position: absolute` but had no width, so
it shrank to
  fit its content instead of filling the foldview.
## @lynx-js/react-webpack-plugin@0.11.1

### Patch Changes

- Collect element templates from the background build as well, so a
template the main-thread bundle does not reach is still registered for
it. ([#3563](#3563))
## @lynx-js/template-webpack-plugin@0.15.2

### Patch Changes

- Updated dependencies
[[`9c2be3e`](9c2be3e),
[`cf98e14`](cf98e14),
[`3bf135c`](3bf135c),
[`ae25e93`](ae25e93),
[`9c2be3e`](9c2be3e)]:
  - @lynx-js/css-serializer@0.1.9
  - @lynx-js/web-core@0.25.0
## @lynx-js/react-umd@0.125.0


## create-rspeedy@0.16.5


## @lynx-js/react-alias-rsbuild-plugin@0.19.1


## upgrade-rspeedy@0.16.5


## @lynx-js/web-rsbuild-server-middleware@0.25.0


## @lynx-js/web-worker-rpc@0.25.0

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
upupming added a commit that referenced this pull request Sep 17, 2026
Since #3596, `pluginLynx` registers `pluginLynxDebugMetadata`, but the
README and TSDoc still said Rspeedy core auto-registers it. They now say
`pluginLynx` registers it, which environments get `debug-metadata.json`,
and that a plugin driving `LynxTemplatePlugin` without the exposure is
skipped rather than failing. No runtime change.

## Checklist

<!--- Check and mark with an "x" -->

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


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Documentation**
- Clarified that debug metadata is registered through `pluginLynx` and
related DSL plugins.
- Documented when `debug-metadata.json` is emitted, including Lynx
template builds, local production builds, and `DEBUG` settings.
- Updated guidance for custom integrations that use the template plugin
without `pluginLynx`.
- Added documentation for the `DEBUG_METADATA_ASSET_NAME` and
`rewriteSourceMappingURLs` exports.
- Updated README and API documentation to reflect the current behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
yifancong pushed a commit to yifancong/lynx-stack that referenced this pull request Sep 24, 2026
…y#3959)

Since lynx-family#3596, `pluginLynx` registers `pluginLynxDebugMetadata`, but the
README and TSDoc still said Rspeedy core auto-registers it. They now say
`pluginLynx` registers it, which environments get `debug-metadata.json`,
and that a plugin driving `LynxTemplatePlugin` without the exposure is
skipped rather than failing. No runtime change.

## Checklist

<!--- Check and mark with an "x" -->

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


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Documentation**
- Clarified that debug metadata is registered through `pluginLynx` and
related DSL plugins.
- Documented when `debug-metadata.json` is emitted, including Lynx
template builds, local production builds, and `DEBUG` settings.
- Updated guidance for custom integrations that use the template plugin
without `pluginLynx`.
- Added documentation for the `DEBUG_METADATA_ASSET_NAME` and
`rewriteSourceMappingURLs` exports.
- Updated README and API documentation to reflect the current behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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