fix: snapshot not found error when dev with external bundle#2316
fix: snapshot not found error when dev with external bundle#2316
Conversation
🦋 Changeset detectedLatest commit: 7c2376b The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
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 |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR fixes snapshot not found errors in development mode with external bundles by implementing conditional minification based on NODE_ENV, adding runtime module injection for consumer module loading, and introducing guards for CSS stylesheet loading in external bundle contexts. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~28 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Merging this PR will improve performance by 7.78%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | basic-performance-scroll-view-100 |
9.2 ms | 8.7 ms | +6.22% |
| ⚡ | basic-performance-nest-level-100 |
7.4 ms | 6.9 ms | +7.78% |
Comparing fix/try-fix-externals-hmr (7c2376b) with main (7fe71ea)
Footnotes
-
3 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. ↩
Web Explorer#8116 Bundle Size — 384.5KiB (0%).7c2376b(current) vs 7fe71ea main#8113(baseline) Bundle metrics
|
| Current #8116 |
Baseline #8113 |
|
|---|---|---|
155.59KiB |
155.59KiB |
|
35.1KiB |
35.1KiB |
|
0% |
0% |
|
8 |
8 |
|
8 |
8 |
|
237 |
237 |
|
16 |
16 |
|
2.98% |
2.98% |
|
4 |
4 |
|
0 |
0 |
Bundle size by type no changes
| Current #8116 |
Baseline #8113 |
|
|---|---|---|
253.55KiB |
253.55KiB |
|
95.85KiB |
95.85KiB |
|
35.1KiB |
35.1KiB |
Bundle analysis report Branch fix/try-fix-externals-hmr Project dashboard
Generated by RelativeCI Documentation Report issue
4086049 to
1e33f85
Compare
1e33f85 to
b5ec4b0
Compare
21e426d to
0cb1d71
Compare
0cb1d71 to
21e426d
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/webpack/react-refresh-webpack-plugin/src/ReactRefreshRspackPlugin.ts (1)
119-125:⚠️ Potential issue | 🟠 MajorUse
modeas the source of truth once, then pass it through.Lines 120-121 and 152-153 use
NODE_ENV || mode, so an explicit production compile can still enable refresh when the shell exportsNODE_ENV=development. Recomputing the flag inside#appendInterceptRuntimeModule()also makes the generated code depend on ambient state instead of the decision made inapply(). DeriveisDevonce fromcompiler.options.mode(with an env fallback only whenmodeis unset, if you still need it) and pass that boolean into the helper.Suggested fix
apply(compiler: Compiler): void { - const isDev = process.env['NODE_ENV'] === 'development' - || compiler.options.mode === 'development'; + const isDev = compiler.options.mode === undefined + ? process.env['NODE_ENV'] === 'development' + : compiler.options.mode === 'development'; @@ - this.#appendInterceptRuntimeModule(compiler, runtimeModule); + this.#appendInterceptRuntimeModule(runtimeModule, isDev); @@ - `#appendInterceptRuntimeModule`( - compiler: Compiler, - runtimeModule: RuntimeModule, - ) { - const isDev = process.env['NODE_ENV'] === 'development' - || compiler.options.mode === 'development'; + `#appendInterceptRuntimeModule`( + runtimeModule: RuntimeModule, + isDev: boolean, + ) {Also applies to: 139-145, 148-165
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/webpack/react-refresh-webpack-plugin/src/ReactRefreshRspackPlugin.ts` around lines 119 - 125, Compute the development flag once from compiler.options.mode (use process.env.NODE_ENV only as a fallback when mode is undefined), assign it to isDev inside apply(), and pass that boolean into `#appendInterceptRuntimeModule` instead of re-evaluating NODE_ENV there; update any other spots that recompute the flag (the blocks around the previous lines 139-145 and 148-165) to use this single isDev value so generated code depends only on the decision made in apply() and not ambient state.
🧹 Nitpick comments (3)
packages/webpack/react-refresh-webpack-plugin/src/ReactRefreshWebpackPlugin.ts (1)
148-155: Minor duplication: reuse existingisDevvariable.The
isDevvalue is already computed at lines 99-100 in the outer scope. The inner computation at lines 152-153 is redundant since theRefreshRuntimeModuleclass has access to the outer scope via closure.♻️ Suggested simplification
.replaceAll('$MAIN_THREAD_LAYER$', LAYERS.MAIN_THREAD) .replaceAll('$BACKGROUND_LAYER$', LAYERS.BACKGROUND) .replaceAll( '__DEV__', - JSON.stringify( - process.env['NODE_ENV'] === 'development' - || compiler.options.mode === 'development', - ), + JSON.stringify(isDev), );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/webpack/react-refresh-webpack-plugin/src/ReactRefreshWebpackPlugin.ts` around lines 148 - 155, The code recomputes the development flag instead of reusing the existing isDev; update the replacement in RefreshRuntimeModule to use the outer-scope isDev variable rather than re-evaluating process.env['NODE_ENV'] || compiler.options.mode. Locate the replaceAll call that substitutes '__DEV__' inside the RefreshRuntimeModule context and replace the inner JSON.stringify(...) expression with JSON.stringify(isDev) so the module uses the previously computed isDev value.examples/react-externals/package.json (1)
11-12: Consider cross-platform compatibility forNODE_ENVassignment.The
NODE_ENV=developmentprefix works on Unix-like systems but not on Windows. Consider usingcross-envfor cross-platform support if Windows development is expected.💡 Suggested fix using cross-env
- "dev:comp-lib": "NODE_ENV=development rslib build --config rslib-comp-lib.config.ts", - "dev:reactlynx": "NODE_ENV=development rslib build --config rslib-reactlynx.config.ts" + "dev:comp-lib": "cross-env NODE_ENV=development rslib build --config rslib-comp-lib.config.ts", + "dev:reactlynx": "cross-env NODE_ENV=development rslib build --config rslib-reactlynx.config.ts"This would require adding
cross-envas a devDependency.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/react-externals/package.json` around lines 11 - 12, Update the npm scripts that set NODE_ENV to be cross-platform: replace the current Unix-only prefixes used in the "dev:comp-lib" and "dev:reactlynx" scripts with a cross-platform prefix by adding cross-env as a devDependency and prefixing those script commands with cross-env NODE_ENV=development so they work on Windows and Unix alike.packages/webpack/react-refresh-webpack-plugin/test/intercept.test.ts (1)
135-216: Please cover the real production path too.These cases flip
modeto development beforeapply(), so they only exercise the__DEV__substitution insidegenerate(). A regression where the plugin still installs the intercept runtime in an actualmode: 'production'build—or only leaks whenNODE_ENVandmodedisagree—would still pass here. Add one case that keeps production mode from the start, stubsNODE_ENVexplicitly, and expects no runtime module / no__LYNX_WEBPACK_MODULES__snippet.Also applies to: 218-267
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/webpack/react-refresh-webpack-plugin/test/intercept.test.ts` around lines 135 - 216, Add a complementary test that exercises the real production path by instantiating ReactRefreshWebpackPlugin and calling plugin.apply with mockCompiler.options.mode set to 'production' before apply(), stub process.env.NODE_ENV to 'production' for the test, and verify that addRuntimeModule is not invoked (generateResult remains empty) and the generated string does NOT contain the __LYNX_WEBPACK_MODULES__ snippet; update the existing test harness using the same mockCompiler shape (hooks.compilation.tap, addRuntimeModule, RuntimeGlobals.interceptModuleExecution) and mirror this change for the similar case referenced around lines 218-267 so both development-early-return and true-production paths are covered.
🤖 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/MainThreadRuntimeWrapperWebpackPlugin.ts`:
- Around line 73-97: The current isDev check wrongly treats
process.env['NODE_ENV'] as equal weight with compiler.options.mode so a
production build can be contaminated if NODE_ENV=development; change the isDev
logic to make compiler.options.mode authoritative by checking
compiler.options.mode === 'development' first and only falling back to
process.env['NODE_ENV'] when compiler.options.mode is unset/undefined (affecting
the isDev variable used before compiler.hooks.thisCompilation and the runtime
module addition via LoadingConsumerModulesRuntimeModule and
RuntimeGlobals.interceptModuleExecution).
- Around line 62-68: The current __webpack_require__.i.push handler
unconditionally overwrites options.factory, which can clobber local module
implementations; modify the handler added via __webpack_require__.i.push to only
set options.factory from
globalThis[Symbol.for('__LYNX_WEBPACK_MODULES__')][moduleId] when
options.factory is missing/falsey (e.g., if (!options.factory) { options.factory
= globalModules[moduleId]; }), keeping the rest of the logic (moduleId and
globalModules lookup) intact.
---
Outside diff comments:
In
`@packages/webpack/react-refresh-webpack-plugin/src/ReactRefreshRspackPlugin.ts`:
- Around line 119-125: Compute the development flag once from
compiler.options.mode (use process.env.NODE_ENV only as a fallback when mode is
undefined), assign it to isDev inside apply(), and pass that boolean into
`#appendInterceptRuntimeModule` instead of re-evaluating NODE_ENV there; update
any other spots that recompute the flag (the blocks around the previous lines
139-145 and 148-165) to use this single isDev value so generated code depends
only on the decision made in apply() and not ambient state.
---
Nitpick comments:
In `@examples/react-externals/package.json`:
- Around line 11-12: Update the npm scripts that set NODE_ENV to be
cross-platform: replace the current Unix-only prefixes used in the
"dev:comp-lib" and "dev:reactlynx" scripts with a cross-platform prefix by
adding cross-env as a devDependency and prefixing those script commands with
cross-env NODE_ENV=development so they work on Windows and Unix alike.
In
`@packages/webpack/react-refresh-webpack-plugin/src/ReactRefreshWebpackPlugin.ts`:
- Around line 148-155: The code recomputes the development flag instead of
reusing the existing isDev; update the replacement in RefreshRuntimeModule to
use the outer-scope isDev variable rather than re-evaluating
process.env['NODE_ENV'] || compiler.options.mode. Locate the replaceAll call
that substitutes '__DEV__' inside the RefreshRuntimeModule context and replace
the inner JSON.stringify(...) expression with JSON.stringify(isDev) so the
module uses the previously computed isDev value.
In `@packages/webpack/react-refresh-webpack-plugin/test/intercept.test.ts`:
- Around line 135-216: Add a complementary test that exercises the real
production path by instantiating ReactRefreshWebpackPlugin and calling
plugin.apply with mockCompiler.options.mode set to 'production' before apply(),
stub process.env.NODE_ENV to 'production' for the test, and verify that
addRuntimeModule is not invoked (generateResult remains empty) and the generated
string does NOT contain the __LYNX_WEBPACK_MODULES__ snippet; update the
existing test harness using the same mockCompiler shape (hooks.compilation.tap,
addRuntimeModule, RuntimeGlobals.interceptModuleExecution) and mirror this
change for the similar case referenced around lines 218-267 so both
development-early-return and true-production paths are covered.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f6d746b0-8063-4469-9412-30822bd407a3
📒 Files selected for processing (13)
.changeset/six-readers-cover.mdexamples/react-externals/package.jsonexamples/react-externals/rslib-comp-lib.config.tsexamples/react-externals/rslib-reactlynx.config.tspackages/rspeedy/lynx-bundle-rslib-config/etc/lynx-bundle-rslib-config.api.mdpackages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.tspackages/rspeedy/lynx-bundle-rslib-config/src/webpack/MainThreadRuntimeWrapperWebpackPlugin.tspackages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.tspackages/webpack/externals-loading-webpack-plugin/src/index.tspackages/webpack/react-refresh-webpack-plugin/runtime/intercept.cjspackages/webpack/react-refresh-webpack-plugin/src/ReactRefreshRspackPlugin.tspackages/webpack/react-refresh-webpack-plugin/src/ReactRefreshWebpackPlugin.tspackages/webpack/react-refresh-webpack-plugin/test/intercept.test.ts
packages/rspeedy/lynx-bundle-rslib-config/src/webpack/MainThreadRuntimeWrapperWebpackPlugin.ts
Show resolved
Hide resolved
packages/rspeedy/lynx-bundle-rslib-config/src/webpack/MainThreadRuntimeWrapperWebpackPlugin.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
packages/rspeedy/lynx-bundle-rslib-config/src/webpack/MainThreadRuntimeWrapperWebpackPlugin.ts (1)
80-80: Use consistent tap name.Line 80 uses
'MyRuntimePlugin'while lines 82 and 91 usePLUGIN_NAME. This inconsistency appears to be a leftover from refactoring. Using consistent tap names improves debuggability in webpack's plugin tracing.♻️ Suggested fix
if (isDev) { - compiler.hooks.thisCompilation.tap('MyRuntimePlugin', (compilation) => { + compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => { compilation.hooks.additionalTreeRuntimeRequirements.tap(🤖 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/MainThreadRuntimeWrapperWebpackPlugin.ts` at line 80, Replace the hardcoded tap name 'MyRuntimePlugin' with the PLUGIN_NAME constant to keep tap names consistent; update the compiler.hooks.thisCompilation.tap call inside MainThreadRuntimeWrapperWebpackPlugin (the line using 'MyRuntimePlugin') to use PLUGIN_NAME so it matches the other taps (e.g., the ones at lines referencing PLUGIN_NAME) and improves webpack plugin tracing.
🤖 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/webpack/react-refresh-webpack-plugin/test/intercept.test.ts`:
- Around line 135-176: The test mutates process.env['NODE_ENV'] but only
restores it after execution; wrap the mutation and the plugin.apply/assertion in
a try/finally so NODE_ENV is always restored even if plugin.apply() or expect()
throws—change the two tests that set NODE_ENV to 'production' (the one calling
ReactRefreshWebpackPlugin().apply(mockCompiler) and the similar test at 179-205)
to save originalEnv, set NODE_ENV, run the test body in try, and restore
process.env['NODE_ENV'] = originalEnv in finally.
- Around line 135-176: Update the test to assert the production transform
emitted by the plugin instead of only checking that the hook wasn't tapped: in
the ReactRefreshWebpackPlugin test replace the current early-return assertion
with logic that captures the generated runtime source (the intercept runtime,
e.g. the generated intercept.cjs content produced when
plugin.apply(mockCompiler) is invoked) from the mocked compilation/runtime
module and assert it contains the production-optimized conditional (e.g. "if
(false)"). Locate symbols ReactRefreshWebpackPlugin, plugin.apply, the
mockCompiler.hooks.compilation.tap stub and the RuntimeModule mock and change
the tap implementation to capture the runtime source string produced by the
plugin, then replace expect(generateResult).toBe('') with an assertion that the
captured source includes "if (false)"; make the same change for the other test
case referenced (lines 179-205).
---
Nitpick comments:
In
`@packages/rspeedy/lynx-bundle-rslib-config/src/webpack/MainThreadRuntimeWrapperWebpackPlugin.ts`:
- Line 80: Replace the hardcoded tap name 'MyRuntimePlugin' with the PLUGIN_NAME
constant to keep tap names consistent; update the
compiler.hooks.thisCompilation.tap call inside
MainThreadRuntimeWrapperWebpackPlugin (the line using 'MyRuntimePlugin') to use
PLUGIN_NAME so it matches the other taps (e.g., the ones at lines referencing
PLUGIN_NAME) and improves webpack plugin tracing.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ed70a38b-6932-4cbc-a424-bbb35761d65b
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (5)
examples/react-externals/package.jsonpackages/rspeedy/lynx-bundle-rslib-config/src/webpack/MainThreadRuntimeWrapperWebpackPlugin.tspackages/webpack/react-refresh-webpack-plugin/src/ReactRefreshRspackPlugin.tspackages/webpack/react-refresh-webpack-plugin/src/ReactRefreshWebpackPlugin.tspackages/webpack/react-refresh-webpack-plugin/test/intercept.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- examples/react-externals/package.json
- packages/webpack/react-refresh-webpack-plugin/src/ReactRefreshWebpackPlugin.ts
- packages/webpack/react-refresh-webpack-plugin/src/ReactRefreshRspackPlugin.ts
packages/webpack/react-refresh-webpack-plugin/test/intercept.test.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/test/external-bundle.test.ts`:
- Around line 248-271: The helper buildWithNodeEnv mutates
process.env['NODE_ENV'] but only restores it after awaiting build() and
decodeTemplate(), so if an error is thrown the env stays mutated; wrap the
awaits in a try/finally inside buildWithNodeEnv (using the existing prevNodeEnv)
and move process.env['NODE_ENV'] = prevNodeEnv into the finally block so build()
and decodeTemplate() are executed inside try and NODE_ENV is always restored.
- Around line 202-230: The test that stubs NODE_ENV via vi.stubEnv('NODE_ENV',
'development') should restore the environment after running to avoid leaking
into later tests; update the "should include LoadingConsumerModulesRuntimeModule
in the main-thread bundle" test (the block that calls vi.stubEnv) to wrap its
body in try/finally and call vi.unstubAllEnvs() in the finally clause so
subsequent helpers like buildWithNodeEnv/readers see the original NODE_ENV; keep
the existing build(...) and decodeTemplate(...) assertions intact and only add
the try/finally + vi.unstubAllEnvs() cleanup around them.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 8b488ef7-5477-4420-afde-ffc0045d0110
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (7)
examples/react-externals/package.jsonexamples/react-externals/rslib-comp-lib.config.tsexamples/react-externals/rslib-reactlynx.config.tspackages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.tspackages/rspeedy/lynx-bundle-rslib-config/src/webpack/MainThreadRuntimeWrapperWebpackPlugin.tspackages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.tspackages/webpack/externals-loading-webpack-plugin/src/index.ts
💤 Files with no reviewable changes (2)
- examples/react-externals/rslib-comp-lib.config.ts
- examples/react-externals/rslib-reactlynx.config.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- packages/rspeedy/lynx-bundle-rslib-config/src/webpack/MainThreadRuntimeWrapperWebpackPlugin.ts
- examples/react-externals/package.json
- packages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.ts
- packages/webpack/externals-loading-webpack-plugin/src/index.ts
packages/rspeedy/lynx-bundle-rslib-config/test/external-bundle.test.ts
Outdated
Show resolved
Hide resolved
0ee5dac to
0d9d696
Compare
33894de to
747de5f
Compare
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.117.0 ### Minor Changes - feat: export `GlobalPropsProvider`, `GlobalPropsConsumer`, `useGlobalProps` and `useGlobalPropsChanged` for `__globalProps` ([#2346](#2346)) - `GlobalPropsProvider`: A Provider component that accepts `children`. It is used to provide the `lynx.__globalProps` context. - `GlobalPropsConsumer`: A Consumer component that accepts a function as a child. It is used to consume the `lynx.__globalProps` context. - `useGlobalProps`: A hook that returns the `lynx.__globalProps` object. It triggers a re-render when `lynx.__globalProps` changes. - `useGlobalPropsChanged`: A hook that accepts a callback function. The callback is invoked when `lynx.__globalProps` changes. Note: When `globalPropsMode` is not set to `'event'` (default is `'reactive'`), these APIs will be ineffective (pass-through) and will log a warning in development mode, as updates are triggered automatically by full re-render. - **BREAKING CHANGE**: ([#2319](#2319)) Change preact package from `@hongzhiyuan/preact` to `@lynx-js/internal-preact`. Upgrade preact from [f7693b72](preactjs/preact@f7693b7) to [55254ef7](preactjs/preact@55254ef), see diffs at [f7693b72...55254ef7](https://github.com/preactjs/preact/compare/f7693b72ecb4a40c66e6e47f54e2d4edc374c9f0...preactjs:preact:55254ef7021e563cc1a86fb816058964a1b6a29a?expand=1). - feat: add `globalPropsMode` option to `PluginReactLynxOptions` ([#2346](#2346)) - When configured to `"event"`, `updateGlobalProps` will only trigger a global event and skip the `runWithForce` flow. - Defaults to `"reactive"`, which means `updateGlobalProps` will trigger re-render automatically. ### Patch Changes - Add `__BACKGROUND__` guard on `onBackgroundSnapshotInstanceUpdateId` event to prevent bundling to main-thread on dev environment. ([#2332](#2332)) - refactor: extract static string in template literal ([#2334](#2334)) - fix: avoid crash when spread undefined ref ([#2333](#2333)) - Avoid registering lifecycle refs for main-thread functions (MTF) that have not received an `execId` during `renderPage()` first-screen binding. ([#2320](#2320)) ## @lynx-js/react-umd@0.117.0 ### Minor Changes - Add standalone UMD build of the ReactLynx runtime. ([#2331](#2331)) ## @lynx-js/react-rsbuild-plugin@0.13.0 ### Minor Changes - **BREAKING CHANGE**: ([#2319](#2319)) Change preact package from `@hongzhiyuan/preact` to `@lynx-js/internal-preact`. Upgrade preact from [f7693b72](preactjs/preact@f7693b7) to [55254ef7](preactjs/preact@55254ef), see diffs at [f7693b72...55254ef7](https://github.com/preactjs/preact/compare/f7693b72ecb4a40c66e6e47f54e2d4edc374c9f0...preactjs:preact:55254ef7021e563cc1a86fb816058964a1b6a29a?expand=1). - feat: add `globalPropsMode` option to `PluginReactLynxOptions` ([#2346](#2346)) - When configured to `"event"`, `updateGlobalProps` will only trigger a global event and skip the `runWithForce` flow. - Defaults to `"reactive"`, which means `updateGlobalProps` will trigger re-render automatically. ### Patch Changes - Updated dependencies \[[`f1129ea`](f1129ea), [`27f1cff`](27f1cff), [`ed566f0`](ed566f0), [`402ec2b`](402ec2b)]: - @lynx-js/react-webpack-plugin@0.8.0 - @lynx-js/react-refresh-webpack-plugin@0.3.5 - @lynx-js/react-alias-rsbuild-plugin@0.13.0 - @lynx-js/use-sync-external-store@1.5.0 - @lynx-js/template-webpack-plugin@0.10.6 - @lynx-js/css-extract-webpack-plugin@0.7.0 ## @lynx-js/react-webpack-plugin@0.8.0 ### Minor Changes - feat: add `globalPropsMode` option to `PluginReactLynxOptions` ([#2346](#2346)) - When configured to `"event"`, `updateGlobalProps` will only trigger a global event and skip the `runWithForce` flow. - Defaults to `"reactive"`, which means `updateGlobalProps` will trigger re-render automatically. ### Patch Changes - Fix sourcemap misalignment when wrapping lazy bundle main-thread chunks. ([#2361](#2361)) The lazy bundle IIFE wrapper is now injected in `processAssets` at `PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE + 1` by walking chunk groups instead of patching assets in `beforeEncode`. - With `experimental_isLazyBundle: true`, the wrapper is applied to lazy-bundle chunk groups. - Without lazy bundle mode, the wrapper is applied to async main-thread chunk groups generated by dynamic import. Injecting the wrapper in this stage keeps the emitted JS stable after optimization while still running before `DEV_TOOLING` sourcemap finalization, so the generated `.js` and `.js.map` stay aligned. - Set `__DEV__` and `__PROFILE__` to `true` on `NODE_ENV === 'development'`. ([#2324](#2324)) ## @lynx-js/rspeedy@0.13.6 ### Patch Changes - Rename Web Preview label to fix URL alignment ([#2355](#2355)) - Updated dependencies \[[`799fda8`](799fda8)]: - @lynx-js/cache-events-webpack-plugin@0.0.3 - @lynx-js/web-rsbuild-server-middleware@0.19.9 ## @lynx-js/lynx-bundle-rslib-config@0.2.3 ### Patch Changes - Fix snapshot not found error when dev with external bundle ([#2316](#2316)) ## @lynx-js/external-bundle-rsbuild-plugin@0.0.4 ### Patch Changes - Updated dependencies \[[`ed566f0`](ed566f0)]: - @lynx-js/externals-loading-webpack-plugin@0.0.5 ## @lynx-js/kitten-lynx-test-infra@0.1.1 ### Patch Changes - feat: support page.screenshot() ([#2364](#2364)) - feat: initial commit ([#2272](#2272)) ## @lynx-js/testing-environment@0.1.12 ### Patch Changes - Implement `__ElementAnimate` PAPI for web platform animation lifecycle ([#2329](#2329)) ## @lynx-js/web-constants@0.19.9 ### Patch Changes - Implement `__ElementAnimate` PAPI for web platform animation lifecycle ([#2329](#2329)) - Updated dependencies \[]: - @lynx-js/web-worker-rpc@0.19.9 ## @lynx-js/web-core@0.19.9 ### Patch Changes - Updated dependencies \[[`2efecc2`](2efecc2)]: - @lynx-js/web-constants@0.19.9 - @lynx-js/web-mainthread-apis@0.19.9 - @lynx-js/web-worker-runtime@0.19.9 - @lynx-js/web-worker-rpc@0.19.9 ## @lynx-js/web-core-wasm@0.0.6 ### Patch Changes - reexports essential utils & types in @lynx-js/web-elements from @lynx-js/web-core-wasm/client ([#2321](#2321)) - Updated dependencies \[]: - @lynx-js/web-worker-rpc@0.19.9 ## @lynx-js/web-mainthread-apis@0.19.9 ### Patch Changes - Updated dependencies \[[`2efecc2`](2efecc2)]: - @lynx-js/web-constants@0.19.9 ## @lynx-js/web-worker-runtime@0.19.9 ### Patch Changes - Updated dependencies \[[`2efecc2`](2efecc2)]: - @lynx-js/web-constants@0.19.9 - @lynx-js/web-mainthread-apis@0.19.9 - @lynx-js/web-worker-rpc@0.19.9 ## @lynx-js/cache-events-webpack-plugin@0.0.3 ### Patch Changes - Cache `globalThis.loadDynamicComponent` in the cache events runtime and add tests covering tt methods, performance events, and globalThis replay behavior. ([#2343](#2343)) ## @lynx-js/externals-loading-webpack-plugin@0.0.5 ### Patch Changes - Fix snapshot not found error when dev with external bundle ([#2316](#2316)) ## @lynx-js/react-refresh-webpack-plugin@0.3.5 ### Patch Changes - Fix snapshot not found error when dev with external bundle ([#2316](#2316)) ## @lynx-js/template-webpack-plugin@0.10.6 ### Patch Changes - Updated dependencies \[[`d034dae`](d034dae)]: - @lynx-js/web-core-wasm@0.0.6 ## create-rspeedy@0.13.6 ## @lynx-js/react-alias-rsbuild-plugin@0.13.0 ## upgrade-rspeedy@0.13.6 ## @lynx-js/web-core-server@0.19.9 ## @lynx-js/web-rsbuild-server-middleware@0.19.9 ## @lynx-js/web-worker-rpc@0.19.9 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
This PR addresses the "snapshot not found" error that occurs during development with external bundles.
Root Cause:
The underlying issue occurs because
__webpack_modules__is not inherently shared between the host application and external bundles. However, certain development features (like React Refresh and specific module loading logic) rely on cross-boundary module access to function properly, leading to "snapshot not found" when the module cannot be resolved.How we fix it:
To resolve this, we explicitly expose development modules via
globalThis[Symbol.for('__LYNX_WEBPACK_MODULES__')]so that the host and externals can seamlessly share module contexts. Crucially, we ensure this global sharing is strictly limited todevelopmentmode to prevent memory leaks, security issues, or pollution in production.Key Changes:
__LYNX_WEBPACK_MODULES__:intercept.cjsand the core Webpack/Rspack React Refresh plugins to conditionally expose__webpack_modules__ONLY when the compiler mode isdevelopment.production, this code is completely stripped (evaluated asif (false)), preventing production code pollution.LoadingConsumerModulesRuntimeModuleManagement:MyCustomRuntimeModuletoLoadingConsumerModulesRuntimeModule.MainThreadRuntimeWrapperWebpackPlugin.tsso that this runtime module is solely injected in development mode, correctly registering external consumer modules for HMR/refresh purposes without bloating the production bundle.external-bundle.test.tsto strongly assert that macros like__DEV__handledevelopmentandproductionmodes correctly, ensuring distinct artifacts.intercept.test.tsto verify that theglobalThisassignment is effectively removed in production builds across both Rspack and Webpack compiler mocks.Summary by CodeRabbit
Bug Fixes
Improvements
New Features
Checklist