Skip to content

feat: support externals sharing across bundles#2007

Merged
upupming merged 2 commits intolynx-family:mainfrom
upupming:feat/externals-sharing
Dec 19, 2025
Merged

feat: support externals sharing across bundles#2007
upupming merged 2 commits intolynx-family:mainfrom
upupming:feat/externals-sharing

Conversation

@upupming
Copy link
Collaborator

@upupming upupming commented Dec 18, 2025

Summary by CodeRabbit

  • New Features

    • Added support for loading externals produced by the new bundle configuration.
    • Enabled asynchronous loading for the App.js entry in the examples.
  • Refactor

    • Unified the external global reference to a single, consistent access pattern across runtimes.
  • Tests

    • Updated test expectations to match the new external global access pattern.

✏️ Tip: You can customize this high-level summary in your review settings.

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).

@upupming upupming requested a review from colinaaa as a code owner December 18, 2025 12:54
@changeset-bot
Copy link

changeset-bot bot commented Dec 18, 2025

⚠️ No Changeset found

Latest commit: 4336737

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes changesets to release 2 packages
Name Type
@lynx-js/externals-loading-webpack-plugin Patch
@lynx-js/lynx-bundle-rslib-config Patch

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 18, 2025

📝 Walkthrough

Walkthrough

This pull request consolidates per-layer externals handling into a single symbol-based global lynx[Symbol.for("__LYNX_EXTERNAL_GLOBAL__")], removes layer-specific branching and a contextInfo parameter from the externals resolver, updates runtime loading to use the unified global, and adjusts related tests and config.

Changes

Cohort / File(s) Summary
Changeset metadata
\.changeset/chilly-ravens-smile.md
Added a patch entry for @lynx-js/lynx-bundle-rslib-config and expanded the plugin description to mention loading externals built by that package.
Example config
examples/react-externals/lynx.config.js
Changed externals['./App.js'].async from false to true (App.js now marked for async loading).
Core externals resolver
packages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.ts
Simplified transformExternals resolver: removed contextInfo parameter, dropped per-layer branching, and unified injected global to lynx[Symbol.for("__LYNX_EXTERNAL_GLOBAL__")] for all externals.
Webpack plugin runtime
packages/webpack/externals-loading-webpack-plugin/src/index.ts
Removed per-layer global variants and layer parameter from getLynxExternalGlobal(), consolidated async/sync load code paths to use the single symbol-based global and updated runtime initialization and externals construction accordingly.
Test expectations
packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/filter-duplicate-externals/index.js, packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/not-overrides-existed-externals/index.js
Updated assertions to use lynx[Symbol.for('__LYNX_EXTERNAL_GLOBAL__')] instead of previous lynxCoreInject.tt.lynx_ex or globalThis.lynx_ex access patterns.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • packages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.ts: confirm removal of contextInfo and per-layer logic doesn't break any layer-specific expectations or downstream consumers.
    • packages/webpack/externals-loading-webpack-plugin/src/index.ts: verify runtime initialization, loadCode assembly (async/sync consolidation), and emitted code correctly reference the unified symbol global in all code paths.
    • Tests: ensure updated string expectations precisely match emitted runtime code and edge cases (background vs main-thread) remain covered.

Possibly related PRs

Suggested reviewers

  • colinaaa

Poem

🐰 A single symbol I proudly bear,

layers folded into open air,
sync or async, one path to share,
the externals find their cozy lair —
hop on, the bundle's light as air! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: support externals sharing across bundles' directly relates to the main objective of the PR, which involves enabling externals to be shared across bundles through a unified symbol-based global reference mechanism.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
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: 0

🧹 Nitpick comments (1)
packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/filter-duplicate-externals/index.js (1)

19-30: Minor: Inconsistent string concatenation style.

The background check (lines 21-22) uses ["Foo"] + ' = ' while the main-thread check (lines 27-28) uses ["Foo"] + '= '. Both produce the same result, but the inconsistency reduces readability.

🔎 Suggested fix for consistency:
   expect(
     background.split(
-      `lynx[Symbol.for('__LYNX_EXTERNAL_GLOBAL__')]["Foo"]`
-        + ' = createLoadExternalSync(',
+      `lynx[Symbol.for('__LYNX_EXTERNAL_GLOBAL__')]["Foo"] = createLoadExternalSync(`,
     ).length - 1,
   ).toBe(1);
   expect(
     mainThread.split(
-      `lynx[Symbol.for('__LYNX_EXTERNAL_GLOBAL__')]["Foo"] `
-        + '= createLoadExternalSync(',
+      `lynx[Symbol.for('__LYNX_EXTERNAL_GLOBAL__')]["Foo"] = createLoadExternalSync(`,
     ).length - 1,
   ).toBe(1);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37d6aef and aab9a25.

📒 Files selected for processing (6)
  • .changeset/chilly-ravens-smile.md (1 hunks)
  • examples/react-externals/lynx.config.js (1 hunks)
  • packages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.ts (1 hunks)
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts (10 hunks)
  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/filter-duplicate-externals/index.js (1 hunks)
  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/not-overrides-existed-externals/index.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
.changeset/*.md

📄 CodeRabbit inference engine (AGENTS.md)

For contributions, generate and commit a Changeset describing your changes

Files:

  • .changeset/chilly-ravens-smile.md
🧠 Learnings (24)
📓 Common learnings
Learnt from: gaoachao
Repo: lynx-family/lynx-stack PR: 1736
File: .changeset/spotty-experts-smoke.md:1-3
Timestamp: 2025-09-12T09:43:04.847Z
Learning: In the lynx-family/lynx-stack repository, private packages (marked with "private": true in package.json) like lynx-js/react-transform don't require meaningful changeset entries even when their public APIs change, since they are not published externally and only affect internal development.
Learnt from: upupming
Repo: lynx-family/lynx-stack PR: 1616
File: packages/webpack/cache-events-webpack-plugin/test/cases/not-cache-events/lazy-bundle/index.js:3-3
Timestamp: 2025-08-27T12:42:01.095Z
Learning: In webpack, properties like __webpack_require__.lynx_ce are injected during compilation/build time when webpack processes modules and generates bundles, not at runtime when dynamic imports execute. Tests for such properties don't need to wait for dynamic imports to complete.
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1612
File: packages/rspeedy/create-rspeedy/template-react-vitest-rltl-ts/src/tsconfig.json:3-13
Timestamp: 2025-08-27T08:10:09.932Z
Learning: In the lynx-family/lynx-stack repository, Rspeedy templates use `lynx-js/rspeedy/client` types via `rspeedy-env.d.ts` instead of `vite/client` types. Rspeedy provides its own client-side environment type definitions and doesn't require direct Vite type references.
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1523
File: vitest.config.ts:5-6
Timestamp: 2025-08-13T11:46:43.737Z
Learning: In the lynx-stack codebase, default imports are consistently used for Node.js built-in modules (e.g., `import os from 'node:os'`, `import fs from 'node:fs'`). The TypeScript configuration supports esModuleInterop and allowSyntheticDefaultImports, making default imports the preferred pattern over namespace imports for Node.js built-ins.
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1497
File: packages/react/transform/tests/__swc_snapshots__/src/swc_plugin_snapshot/mod.rs/basic_full_static.js:9-10
Timestamp: 2025-08-12T16:09:32.413Z
Learning: In the Lynx stack, functions prefixed with `__` that are called in transformed code may be injected globally by the Lynx Engine at runtime rather than exported from the React runtime package. For example, `__CreateFrame` is injected globally by the Lynx Engine, not exported from lynx-js/react.
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1454
File: pnpm-workspace.yaml:46-46
Timestamp: 2025-08-07T04:00:59.645Z
Learning: In the lynx-family/lynx-stack repository, the webpack patch (patches/webpack5.101.0.patch) was created to fix issues with webpack5.99.9 but only takes effect on webpack5.100.0 and later versions. The patchedDependencies entry should use "webpack@^5.100.0" to ensure the patch applies to the correct version range.
Learnt from: upupming
Repo: lynx-family/lynx-stack PR: 1370
File: packages/webpack/cache-events-webpack-plugin/src/LynxCacheEventsRuntimeModule.ts:23-27
Timestamp: 2025-08-21T08:46:54.494Z
Learning: In Lynx webpack runtime modules, the team prioritizes performance and simplicity over defensive runtime error handling. They prefer relying on compile-time type safety (TypeScript) rather than adding runtime checks like try-catch blocks or type validation, especially for performance-critical code like cache event setup/cleanup functions.
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1558
File: .changeset/solid-squids-fall.md:2-2
Timestamp: 2025-08-19T11:25:36.127Z
Learning: In the lynx-family/lynx-stack repository, changesets should use the exact package name from package.json#name, not generic or unscoped names. Each package has its own specific scoped name (e.g., "lynx-js/react-transform" for packages/react/transform).
📚 Learning: 2025-08-27T12:42:01.095Z
Learnt from: upupming
Repo: lynx-family/lynx-stack PR: 1616
File: packages/webpack/cache-events-webpack-plugin/test/cases/not-cache-events/lazy-bundle/index.js:3-3
Timestamp: 2025-08-27T12:42:01.095Z
Learning: In webpack, properties like __webpack_require__.lynx_ce are injected during compilation/build time when webpack processes modules and generates bundles, not at runtime when dynamic imports execute. Tests for such properties don't need to wait for dynamic imports to complete.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/not-overrides-existed-externals/index.js
  • examples/react-externals/lynx.config.js
  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/filter-duplicate-externals/index.js
  • .changeset/chilly-ravens-smile.md
  • packages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.ts
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-06T13:28:57.182Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1453
File: vitest.config.ts:49-61
Timestamp: 2025-08-06T13:28:57.182Z
Learning: In the lynx-family/lynx-stack repository, the file `packages/react/testing-library/src/vitest.config.js` is source code for the testing library that gets exported for users, not a test configuration that should be included in the main vitest projects array.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/not-overrides-existed-externals/index.js
  • examples/react-externals/lynx.config.js
  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/filter-duplicate-externals/index.js
📚 Learning: 2025-08-11T05:57:18.212Z
Learnt from: upupming
Repo: lynx-family/lynx-stack PR: 1305
File: packages/testing-library/testing-environment/src/index.ts:255-258
Timestamp: 2025-08-11T05:57:18.212Z
Learning: In the ReactLynx testing environment (`packages/testing-library/testing-environment/src/index.ts`), the dual assignment pattern `target.console.method = console.method = () => {}` is required for rstest compatibility. This is because rstest provides `console` in an IIFE (Immediately Invoked Function Expression), and both the target and global console need to have these methods defined for proper test execution.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/not-overrides-existed-externals/index.js
  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/filter-duplicate-externals/index.js
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-09-12T09:43:04.847Z
Learnt from: gaoachao
Repo: lynx-family/lynx-stack PR: 1736
File: .changeset/spotty-experts-smoke.md:1-3
Timestamp: 2025-09-12T09:43:04.847Z
Learning: In the lynx-family/lynx-stack repository, private packages (marked with "private": true in package.json) like lynx-js/react-transform don't require meaningful changeset entries even when their public APIs change, since they are not published externally and only affect internal development.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/not-overrides-existed-externals/index.js
  • examples/react-externals/lynx.config.js
  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/filter-duplicate-externals/index.js
  • .changeset/chilly-ravens-smile.md
  • packages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.ts
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-09-23T08:54:39.966Z
Learnt from: upupming
Repo: lynx-family/lynx-stack PR: 1670
File: packages/webpack/css-extract-webpack-plugin/test/hotCases/hot/hot-update-json/dual-thread/__snapshot__/index.css:6-8
Timestamp: 2025-09-23T08:54:39.966Z
Learning: In the lynx-stack CSS extract webpack plugin tests, many test fixture CSS files intentionally use invalid CSS syntax like `color: 'red';` with quoted values. The snapshots correctly reflect this invalid CSS from the source fixtures. To fix CSS validation issues, the source fixture files should be updated first, then snapshots regenerated, rather than manually editing snapshots.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/not-overrides-existed-externals/index.js
  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/filter-duplicate-externals/index.js
📚 Learning: 2025-08-13T11:46:43.737Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1523
File: vitest.config.ts:5-6
Timestamp: 2025-08-13T11:46:43.737Z
Learning: In the lynx-stack codebase, default imports are consistently used for Node.js built-in modules (e.g., `import os from 'node:os'`, `import fs from 'node:fs'`). The TypeScript configuration supports esModuleInterop and allowSyntheticDefaultImports, making default imports the preferred pattern over namespace imports for Node.js built-ins.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/not-overrides-existed-externals/index.js
  • examples/react-externals/lynx.config.js
  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/filter-duplicate-externals/index.js
  • .changeset/chilly-ravens-smile.md
  • packages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.ts
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-11T05:59:28.530Z
Learnt from: upupming
Repo: lynx-family/lynx-stack PR: 1305
File: packages/react/testing-library/src/plugins/vitest.ts:4-6
Timestamp: 2025-08-11T05:59:28.530Z
Learning: In the lynx-family/lynx-stack repository, the `packages/react/testing-library` package does not have `vite` as a direct dependency. It relies on `vitest` being available from the monorepo root and accesses Vite types through re-exports from `vitest/node`. Direct imports from `vite` should not be suggested for this package.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/not-overrides-existed-externals/index.js
  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/filter-duplicate-externals/index.js
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-11T06:00:04.376Z
Learnt from: upupming
Repo: lynx-family/lynx-stack PR: 1305
File: packages/react/testing-library/src/plugins/vitest.ts:59-61
Timestamp: 2025-08-11T06:00:04.376Z
Learning: In the lynx-family/lynx-stack repository, the `testingLibraryPlugin` in `packages/react/testing-library/src/plugins/vitest.ts` intentionally uses `process.exit` when jsdom installation fails, maintaining consistency with the previous implementation from `packages/react/testing-library/src/vitest.config.js`. This behavior should not be changed to use `this.error` despite being a Vite plugin best practice.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/not-overrides-existed-externals/index.js
📚 Learning: 2025-08-07T04:00:59.645Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1454
File: pnpm-workspace.yaml:46-46
Timestamp: 2025-08-07T04:00:59.645Z
Learning: In the lynx-family/lynx-stack repository, the webpack patch (patches/webpack5.101.0.patch) was created to fix issues with webpack5.99.9 but only takes effect on webpack5.100.0 and later versions. The patchedDependencies entry should use "webpack@^5.100.0" to ensure the patch applies to the correct version range.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/not-overrides-existed-externals/index.js
  • .changeset/chilly-ravens-smile.md
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-12T16:09:32.413Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1497
File: packages/react/transform/tests/__swc_snapshots__/src/swc_plugin_snapshot/mod.rs/basic_full_static.js:9-10
Timestamp: 2025-08-12T16:09:32.413Z
Learning: In the Lynx stack, functions prefixed with `__` that are called in transformed code may be injected globally by the Lynx Engine at runtime rather than exported from the React runtime package. For example, `__CreateFrame` is injected globally by the Lynx Engine, not exported from lynx-js/react.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/not-overrides-existed-externals/index.js
  • examples/react-externals/lynx.config.js
  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/filter-duplicate-externals/index.js
  • packages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.ts
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-21T08:46:54.494Z
Learnt from: upupming
Repo: lynx-family/lynx-stack PR: 1370
File: packages/webpack/cache-events-webpack-plugin/src/LynxCacheEventsRuntimeModule.ts:23-27
Timestamp: 2025-08-21T08:46:54.494Z
Learning: In Lynx webpack runtime modules, the team prioritizes performance and simplicity over defensive runtime error handling. They prefer relying on compile-time type safety (TypeScript) rather than adding runtime checks like try-catch blocks or type validation, especially for performance-critical code like cache event setup/cleanup functions.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/not-overrides-existed-externals/index.js
  • .changeset/chilly-ravens-smile.md
  • packages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.ts
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-19T11:25:36.127Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1558
File: .changeset/solid-squids-fall.md:2-2
Timestamp: 2025-08-19T11:25:36.127Z
Learning: In the lynx-family/lynx-stack repository, changesets should use the exact package name from package.json#name, not generic or unscoped names. Each package has its own specific scoped name (e.g., "lynx-js/react-transform" for packages/react/transform).

Applied to files:

  • examples/react-externals/lynx.config.js
  • .changeset/chilly-ravens-smile.md
  • packages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.ts
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-06T13:28:57.182Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1453
File: vitest.config.ts:49-61
Timestamp: 2025-08-06T13:28:57.182Z
Learning: In the lynx-family/lynx-stack repository, the file `packages/rspeedy/create-rspeedy/template-react-vitest-rltl-js/vitest.config.js` is a template file for scaffolding new Rspeedy projects, not a test configuration that should be included in the main vitest projects array.

Applied to files:

  • examples/react-externals/lynx.config.js
📚 Learning: 2025-07-22T09:26:16.722Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1330
File: .changeset/olive-animals-attend.md:1-3
Timestamp: 2025-07-22T09:26:16.722Z
Learning: In the lynx-family/lynx-stack repository, CI checks require changesets when files matching the pattern "src/**" are modified (as configured in .changeset/config.json). For internal changes that don't need meaningful changesets, an empty changeset file is used to satisfy the CI requirement while not generating any release notes.

Applied to files:

  • examples/react-externals/lynx.config.js
  • .changeset/chilly-ravens-smile.md
📚 Learning: 2025-09-25T14:03:25.576Z
Learnt from: PupilTong
Repo: lynx-family/lynx-stack PR: 1834
File: packages/web-platform/web-worker-runtime/src/backgroundThread/background-apis/createChunkLoading.ts:162-171
Timestamp: 2025-09-25T14:03:25.576Z
Learning: In the lynx-stack codebase, for loadScriptAsync implementations in createChunkLoading.ts, unhandled promise rejections from readScriptAsync are intentionally not caught - the caller is expected to handle errors rather than the loadScriptAsync method itself invoking the callback with error messages.

Applied to files:

  • examples/react-externals/lynx.config.js
  • packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/filter-duplicate-externals/index.js
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-09-12T09:43:04.847Z
Learnt from: gaoachao
Repo: lynx-family/lynx-stack PR: 1736
File: .changeset/spotty-experts-smoke.md:1-3
Timestamp: 2025-09-12T09:43:04.847Z
Learning: In the lynx-family/lynx-stack repository, empty changeset files (containing only `---\n\n---`) are used for internal changes that modify src/** files but don't require meaningful release notes, such as private package changes or testing-only modifications. This satisfies CI requirements without generating user-facing release notes.

Applied to files:

  • .changeset/chilly-ravens-smile.md
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-07-22T09:23:07.797Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1330
File: .changeset/olive-animals-attend.md:1-3
Timestamp: 2025-07-22T09:23:07.797Z
Learning: In the lynx-family/lynx-stack repository, changesets are only required for meaningful changes to end-users such as bugfixes and features. Internal/development changes like chores, refactoring, or removing debug info do not need changeset entries.

Applied to files:

  • .changeset/chilly-ravens-smile.md
📚 Learning: 2025-09-18T04:43:54.426Z
Learnt from: gaoachao
Repo: lynx-family/lynx-stack PR: 1771
File: packages/react/transform/tests/__swc_snapshots__/src/swc_plugin_snapshot/mod.rs/basic_component_with_static_sibling.js:2-2
Timestamp: 2025-09-18T04:43:54.426Z
Learning: In the lynx-family/lynx-stack repository, the `add_pure_comment` function in packages/react/transform/src/swc_plugin_compat/mod.rs (around lines 478-482) is specifically for `wrapWithLynxComponent` calls, not `createSnapshot` calls. The PURE comment injection for `createSnapshot` is handled separately in swc_plugin_snapshot/mod.rs.

Applied to files:

  • .changeset/chilly-ravens-smile.md
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-14T12:54:51.143Z
Learnt from: upupming
Repo: lynx-family/lynx-stack PR: 1370
File: .changeset/brave-melons-add.md:1-7
Timestamp: 2025-08-14T12:54:51.143Z
Learning: In the lynx-family/lynx-stack repository, packages use 0.x.x versioning where minor version bumps indicate breaking changes (not major bumps), following pre-1.0 semantic versioning conventions.

Applied to files:

  • .changeset/chilly-ravens-smile.md
📚 Learning: 2025-08-27T08:10:09.932Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1612
File: packages/rspeedy/create-rspeedy/template-react-vitest-rltl-ts/src/tsconfig.json:3-13
Timestamp: 2025-08-27T08:10:09.932Z
Learning: In the lynx-family/lynx-stack repository, Rspeedy templates use `lynx-js/rspeedy/client` types via `rspeedy-env.d.ts` instead of `vite/client` types. Rspeedy provides its own client-side environment type definitions and doesn't require direct Vite type references.

Applied to files:

  • .changeset/chilly-ravens-smile.md
  • packages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.ts
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-11-06T01:19:23.670Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1917
File: packages/mcp-servers/devtool-mcp-server/tsconfig.json:8-8
Timestamp: 2025-11-06T01:19:23.670Z
Learning: The lynx-js/devtool-mcp-server package in lynx-family/lynx-stack targets Node.js >=18.19 (specified in its package.json engines), which is different from the root project's requirement of Node.js ^22 || ^24. The package uses "lib": ["ES2024.Promise"] in its tsconfig.json because it manually includes polyfills for Promise.withResolvers while maintaining compatibility with Node.js v18.

Applied to files:

  • packages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.ts
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-09-28T08:46:43.177Z
Learnt from: f0rdream
Repo: lynx-family/lynx-stack PR: 1835
File: packages/react/worklet-runtime/src/workletRuntime.ts:52-55
Timestamp: 2025-09-28T08:46:43.177Z
Learning: The legacy worklet path with `_lepusWorkletHash` in `packages/react/worklet-runtime/src/workletRuntime.ts` is preserved for compatibility with MTS (Mini-app Threading Service) that doesn't support Initial Frame Rendering. This path will not be touched in current implementations.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-27T11:37:38.587Z
Learnt from: PupilTong
Repo: lynx-family/lynx-stack PR: 1589
File: packages/web-platform/web-worker-runtime/src/mainThread/startMainThread.ts:36-49
Timestamp: 2025-08-27T11:37:38.587Z
Learning: In web worker script loading (loadScript function), the pattern of calling fetch() before importScripts() is intentional for caching benefits, not redundant networking. The fetch() ensures HTTP caching is utilized before the synchronous importScripts() call.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
🧬 Code graph analysis (1)
packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/not-overrides-existed-externals/index.js (3)
packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/filter-duplicate-externals/index.js (1)
  • mainThread (15-18)
packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/only-background-externals/index.js (1)
  • mainThread (17-20)
packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/only-main-thread-externals/index.js (1)
  • mainThread (16-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build / Build (Ubuntu)
  • GitHub Check: build / Build (Windows)
  • GitHub Check: test-rust / clippy
  • GitHub Check: test-rust / Test (Ubuntu)
🔇 Additional comments (7)
.changeset/chilly-ravens-smile.md (1)

1-29: LGTM!

The changeset properly documents both affected packages with patch versions and provides a clear example of how to use the ExternalsLoadingPlugin. The package names follow the repository's scoped naming convention.

examples/react-externals/lynx.config.js (1)

75-81: LGTM!

The change to async: true for the ./App.js external aligns with the PR's centralized external loading approach. This example demonstrates asynchronous loading of component library externals.

packages/webpack/externals-loading-webpack-plugin/test/cases/externals-loading/not-overrides-existed-externals/index.js (1)

22-27: LGTM!

The test expectations correctly reflect the unified symbol-based global reference pattern. Both background and main-thread now use the same lynx[Symbol.for('__LYNX_EXTERNAL_GLOBAL__')].Foo access pattern, validating the centralized approach.

packages/webpack/externals-loading-webpack-plugin/src/index.ts (3)

217-219: LGTM!

The unified getLynxExternalGlobal() function using Symbol.for('__LYNX_EXTERNAL_GLOBAL__') is a clean approach for cross-bundle sharing. Symbol.for ensures the same symbol is retrieved across different JavaScript contexts, which is essential for externals sharing.


383-400: LGTM!

Both async and sync loading paths correctly use the unified global reference. The use of JSON.stringify(libraryNameStr) for property access ensures proper handling of library names with special characters.


465-472: LGTM!

The externals config correctly returns the unified global reference while still respecting layer-specific applicability through the currentLayer check. This maintains proper external resolution per layer while using the shared global storage.

packages/rspeedy/lynx-bundle-rslib-config/src/externalBundleRslibConfig.ts (1)

90-105: LGTM!

The simplified transformExternals function correctly removes the per-layer branching logic. By using a unified global reference lynx[Symbol.for("__LYNX_EXTERNAL_GLOBAL__")] for all externals, the resolution is consistent while the layer-specific loading behavior is handled by the externals-loading-webpack-plugin at runtime.

@codecov
Copy link

codecov bot commented Dec 18, 2025

Codecov Report

❌ Patch coverage is 66.66667% with 5 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...ndle-rslib-config/src/externalBundleRslibConfig.ts 0.00% 5 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
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

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aab9a25 and 4336737.

📒 Files selected for processing (3)
  • .changeset/chilly-ravens-smile.md (1 hunks)
  • examples/react-externals/lynx.config.js (1 hunks)
  • packages/webpack/externals-loading-webpack-plugin/src/index.ts (10 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/react-externals/lynx.config.js
  • .changeset/chilly-ravens-smile.md
🧰 Additional context used
🧠 Learnings (15)
📓 Common learnings
Learnt from: gaoachao
Repo: lynx-family/lynx-stack PR: 1736
File: .changeset/spotty-experts-smoke.md:1-3
Timestamp: 2025-09-12T09:43:04.847Z
Learning: In the lynx-family/lynx-stack repository, private packages (marked with "private": true in package.json) like lynx-js/react-transform don't require meaningful changeset entries even when their public APIs change, since they are not published externally and only affect internal development.
📚 Learning: 2025-08-27T12:42:01.095Z
Learnt from: upupming
Repo: lynx-family/lynx-stack PR: 1616
File: packages/webpack/cache-events-webpack-plugin/test/cases/not-cache-events/lazy-bundle/index.js:3-3
Timestamp: 2025-08-27T12:42:01.095Z
Learning: In webpack, properties like __webpack_require__.lynx_ce are injected during compilation/build time when webpack processes modules and generates bundles, not at runtime when dynamic imports execute. Tests for such properties don't need to wait for dynamic imports to complete.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-09-12T09:43:04.847Z
Learnt from: gaoachao
Repo: lynx-family/lynx-stack PR: 1736
File: .changeset/spotty-experts-smoke.md:1-3
Timestamp: 2025-09-12T09:43:04.847Z
Learning: In the lynx-family/lynx-stack repository, private packages (marked with "private": true in package.json) like lynx-js/react-transform don't require meaningful changeset entries even when their public APIs change, since they are not published externally and only affect internal development.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-12T16:09:32.413Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1497
File: packages/react/transform/tests/__swc_snapshots__/src/swc_plugin_snapshot/mod.rs/basic_full_static.js:9-10
Timestamp: 2025-08-12T16:09:32.413Z
Learning: In the Lynx stack, functions prefixed with `__` that are called in transformed code may be injected globally by the Lynx Engine at runtime rather than exported from the React runtime package. For example, `__CreateFrame` is injected globally by the Lynx Engine, not exported from lynx-js/react.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-13T11:46:43.737Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1523
File: vitest.config.ts:5-6
Timestamp: 2025-08-13T11:46:43.737Z
Learning: In the lynx-stack codebase, default imports are consistently used for Node.js built-in modules (e.g., `import os from 'node:os'`, `import fs from 'node:fs'`). The TypeScript configuration supports esModuleInterop and allowSyntheticDefaultImports, making default imports the preferred pattern over namespace imports for Node.js built-ins.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-21T08:46:54.494Z
Learnt from: upupming
Repo: lynx-family/lynx-stack PR: 1370
File: packages/webpack/cache-events-webpack-plugin/src/LynxCacheEventsRuntimeModule.ts:23-27
Timestamp: 2025-08-21T08:46:54.494Z
Learning: In Lynx webpack runtime modules, the team prioritizes performance and simplicity over defensive runtime error handling. They prefer relying on compile-time type safety (TypeScript) rather than adding runtime checks like try-catch blocks or type validation, especially for performance-critical code like cache event setup/cleanup functions.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-09-25T14:03:25.576Z
Learnt from: PupilTong
Repo: lynx-family/lynx-stack PR: 1834
File: packages/web-platform/web-worker-runtime/src/backgroundThread/background-apis/createChunkLoading.ts:162-171
Timestamp: 2025-09-25T14:03:25.576Z
Learning: In the lynx-stack codebase, for loadScriptAsync implementations in createChunkLoading.ts, unhandled promise rejections from readScriptAsync are intentionally not caught - the caller is expected to handle errors rather than the loadScriptAsync method itself invoking the callback with error messages.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-27T08:10:09.932Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1612
File: packages/rspeedy/create-rspeedy/template-react-vitest-rltl-ts/src/tsconfig.json:3-13
Timestamp: 2025-08-27T08:10:09.932Z
Learning: In the lynx-family/lynx-stack repository, Rspeedy templates use `lynx-js/rspeedy/client` types via `rspeedy-env.d.ts` instead of `vite/client` types. Rspeedy provides its own client-side environment type definitions and doesn't require direct Vite type references.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-19T11:25:36.127Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1558
File: .changeset/solid-squids-fall.md:2-2
Timestamp: 2025-08-19T11:25:36.127Z
Learning: In the lynx-family/lynx-stack repository, changesets should use the exact package name from package.json#name, not generic or unscoped names. Each package has its own specific scoped name (e.g., "lynx-js/react-transform" for packages/react/transform).

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-11-06T01:19:23.670Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1917
File: packages/mcp-servers/devtool-mcp-server/tsconfig.json:8-8
Timestamp: 2025-11-06T01:19:23.670Z
Learning: The lynx-js/devtool-mcp-server package in lynx-family/lynx-stack targets Node.js >=18.19 (specified in its package.json engines), which is different from the root project's requirement of Node.js ^22 || ^24. The package uses "lib": ["ES2024.Promise"] in its tsconfig.json because it manually includes polyfills for Promise.withResolvers while maintaining compatibility with Node.js v18.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-07T04:00:59.645Z
Learnt from: colinaaa
Repo: lynx-family/lynx-stack PR: 1454
File: pnpm-workspace.yaml:46-46
Timestamp: 2025-08-07T04:00:59.645Z
Learning: In the lynx-family/lynx-stack repository, the webpack patch (patches/webpack5.101.0.patch) was created to fix issues with webpack5.99.9 but only takes effect on webpack5.100.0 and later versions. The patchedDependencies entry should use "webpack@^5.100.0" to ensure the patch applies to the correct version range.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-11T05:59:28.530Z
Learnt from: upupming
Repo: lynx-family/lynx-stack PR: 1305
File: packages/react/testing-library/src/plugins/vitest.ts:4-6
Timestamp: 2025-08-11T05:59:28.530Z
Learning: In the lynx-family/lynx-stack repository, the `packages/react/testing-library` package does not have `vite` as a direct dependency. It relies on `vitest` being available from the monorepo root and accesses Vite types through re-exports from `vitest/node`. Direct imports from `vite` should not be suggested for this package.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-09-18T04:43:54.426Z
Learnt from: gaoachao
Repo: lynx-family/lynx-stack PR: 1771
File: packages/react/transform/tests/__swc_snapshots__/src/swc_plugin_snapshot/mod.rs/basic_component_with_static_sibling.js:2-2
Timestamp: 2025-09-18T04:43:54.426Z
Learning: In the lynx-family/lynx-stack repository, the `add_pure_comment` function in packages/react/transform/src/swc_plugin_compat/mod.rs (around lines 478-482) is specifically for `wrapWithLynxComponent` calls, not `createSnapshot` calls. The PURE comment injection for `createSnapshot` is handled separately in swc_plugin_snapshot/mod.rs.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-08-11T05:57:18.212Z
Learnt from: upupming
Repo: lynx-family/lynx-stack PR: 1305
File: packages/testing-library/testing-environment/src/index.ts:255-258
Timestamp: 2025-08-11T05:57:18.212Z
Learning: In the ReactLynx testing environment (`packages/testing-library/testing-environment/src/index.ts`), the dual assignment pattern `target.console.method = console.method = () => {}` is required for rstest compatibility. This is because rstest provides `console` in an IIFE (Immediately Invoked Function Expression), and both the target and global console need to have these methods defined for proper test execution.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
📚 Learning: 2025-09-28T08:46:43.177Z
Learnt from: f0rdream
Repo: lynx-family/lynx-stack PR: 1835
File: packages/react/worklet-runtime/src/workletRuntime.ts:52-55
Timestamp: 2025-09-28T08:46:43.177Z
Learning: The legacy worklet path with `_lepusWorkletHash` in `packages/react/worklet-runtime/src/workletRuntime.ts` is preserved for compatibility with MTS (Mini-app Threading Service) that doesn't support Initial Frame Rendering. This path will not be touched in current implementations.

Applied to files:

  • packages/webpack/externals-loading-webpack-plugin/src/index.ts
🔇 Additional comments (7)
packages/webpack/externals-loading-webpack-plugin/src/index.ts (7)

207-209: Architectural change: unified global reference.

The removal of the layer parameter consolidates all externals into a single symbol-based global across all layers, which is a significant architectural change from the previous per-layer approach.


269-269: LGTM: Simplified code path consolidation.

The consolidation of asyncLoadCode and syncLoadCode into a single loadCode array simplifies the implementation without changing functionality.


364-370: LGTM: Async loading correctly uses unified global.

The async external loading logic correctly references the unified global. However, this depends on fixing the race condition in the global initialization at line 297.


374-380: LGTM: Sync loading correctly uses unified global.

The sync external loading logic correctly references the unified global. However, this depends on fixing the race condition in the global initialization at line 297.


387-387: LGTM: Code assembly logic is correct.

The flattening and joining of the code segments properly assembles the final runtime code.


107-155: LGTM: Documentation accurately reflects the unified global.

The documentation examples have been correctly updated to show the new lynx[Symbol.for("__LYNX_EXTERNAL_GLOBAL__")] global reference pattern, providing clear guidance for users about how externals are now resolved.


445-471: Verify that the unified global approach works correctly across Lynx layers at runtime.

The externals configuration now maps all externals to a single symbol-based global using lynx[Symbol.for('__LYNX_EXTERNAL_GLOBAL__')]. While Symbol.for() always returns the same Symbol for a given key and works across realms, Lynx uses two separate JavaScript engines—PrimJS for the main thread and a different engine for the background thread. Confirm that both engines share access to the global symbol registry or that the injected lynx object allows both layers to reference the same external global without violating layer isolation guarantees.

@codspeed-hq
Copy link

codspeed-hq bot commented Dec 18, 2025

CodSpeed Performance Report

Merging #2007 will improve performances by 8.32%

Comparing upupming:feat/externals-sharing (4336737) with main (37d6aef)

Summary

⚡ 1 improvement
✅ 62 untouched
⏩ 3 skipped1

Benchmarks breakdown

Benchmark BASE HEAD Change
basic-performance-scroll-view-100 10 ms 9.2 ms +8.32%

Footnotes

  1. 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.

@relativeci
Copy link

relativeci bot commented Dec 18, 2025

React Example

#6579 Bundle Size — 236.9KiB (0%).

4336737(current) vs b256a95 main#6577(baseline)

Bundle metrics  no changes
                 Current
#6579
     Baseline
#6577
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 162 162
No change  Duplicate Modules 65 65
No change  Duplicate Code 46.74% 46.74%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#6579
     Baseline
#6577
No change  IMG 145.76KiB 145.76KiB
No change  Other 91.14KiB 91.14KiB

Bundle analysis reportBranch upupming:feat/externals-sharingProject dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link

relativeci bot commented Dec 18, 2025

Web Explorer

#6739 Bundle Size — 372.63KiB (0%).

4336737(current) vs b256a95 main#6737(baseline)

Bundle metrics  no changes
                 Current
#6739
     Baseline
#6737
No change  Initial JS 146.36KiB 146.36KiB
No change  Initial CSS 32.4KiB 32.4KiB
No change  Cache Invalidation 0% 0%
No change  Chunks 8 8
No change  Assets 8 8
No change  Modules 230 230
No change  Duplicate Modules 16 16
No change  Duplicate Code 2.97% 2.97%
No change  Packages 4 4
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#6739
     Baseline
#6737
No change  JS 243.25KiB 243.25KiB
No change  Other 96.98KiB 96.98KiB
No change  CSS 32.4KiB 32.4KiB

Bundle analysis reportBranch upupming:feat/externals-sharingProject dashboard


Generated by RelativeCIDocumentationReport issue

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