Skip to content

feat(lynx): add production Module Federation support#4907

Open
ScriptedAlchemy wants to merge 46 commits into
mainfrom
feat/lynx-federation
Open

feat(lynx): add production Module Federation support#4907
ScriptedAlchemy wants to merge 46 commits into
mainfrom
feat/lynx-federation

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Jul 16, 2026

Copy link
Copy Markdown
Member

Description

This PR proposes and implements first-class Module Federation support for official Rspeedy + ReactLynx applications through a new @module-federation/lynx package.

The adapter preserves both normal compiled imports (import('catalog/Card')) and the runtime API (loadRemote('catalog/Card')). Remotes are addressed through the standard mf-manifest.json; the manifest points to a public .lynx.bundle container, and exposed modules remain independently HTTP-loadable lazy bundles by default. The reference Catalog is also runnable as a complete standalone native/Web product from the same UI source.

This is an implementation RFC. The sections below define the transport, layer/share semantics, artifact model, compatibility constraints, test evidence, rejected alternatives, and rollout boundaries.

Related Issue

No linked issue yet. The architectural proposal and its executable reference implementation are captured in this PR for maintainer discussion.

RFC: motivation

Lynx is not a browser and cannot use DOM script injection as its remote-entry transport. It also has two JavaScript realms (background and main thread), an official binary bundle format, and a lazy-bundle loader whose registration contract differs from Webpack's browser and Node chunk loaders.

A Lynx integration therefore needs to:

  1. keep Module Federation's manifest, container, share negotiation, and import APIs;
  2. replace only the platform transport and chunk registration seams;
  3. preserve Lynx realm boundaries rather than pretending object identity crosses threads;
  4. avoid shipping all exposes and shared fallbacks in one .lynx.bundle by default;
  5. build through official Rspeedy/ReactLynx plugins and artifacts;
  6. leave existing script, module, CommonJS, and other runtime-core remote types unchanged.

Goals

  • Official Rspeedy plugin integration.
  • Standard manifest-addressed remotes.
  • Compiled federated import() and runtime loadRemote().
  • Native background-realm federation.
  • Paired native ReactLynx UI snapshots inside split DynamicComponent bundles.
  • Dual-realm Lynx for Web federation.
  • Layer-aware exposes and realm-qualified shared scopes.
  • Singleton identity inside one realm/share scope/share key.
  • Split, cacheable remote delivery over HTTP.
  • Retryable entry and lazy-chunk failures with bounded timeouts.
  • A standalone UIKit app, native iOS Simulator E2E, and real official
    <lynx-view> browser E2E.

Non-goals

  • Cross-thread JavaScript object identity. Lynx background and main-thread realms cannot share one in-memory singleton object.
  • Native TASM/MTS execution of the enhanced federation runtime on the main thread.
  • Replacing the Module Federation manifest with a Lynx-specific discovery protocol.
  • Treating the generated JavaScript remoteEntry.js as a public deployment endpoint.
  • Bundling every expose and shared fallback into one binary by default.

Proposed architecture

flowchart LR
  A["Rspeedy application config"] --> B["pluginReactLynx exposes LAYERS and LynxTemplatePlugin"]
  B --> C["pluginLynxModuleFederation"]
  C --> D["Rspack ModuleFederationPlugin"]
  C --> E["layer/share normalization"]
  C --> F["Lynx chunk matcher"]
  C --> G["external bundle + manifest rewrite"]
  D --> H["internal CommonJS container JS"]
  H --> G
  G --> I["mf-manifest.json"]
  G --> J["container.lynx.bundle"]
  G --> K["lazy expose .bundle files"]
Loading

The adapter is registered after the official Lynx DSL plugin. It consumes the DSL's exposed LAYERS.BACKGROUND, LAYERS.MAIN_THREAD, and LynxTemplatePlugin contracts instead of hard-coding internal layer strings.

At compile time it:

  • selects configured Rsbuild environments;
  • enables Rspack layers and Lynx-compatible CommonJS chunk output;
  • assigns exposes and shares to semantic Lynx realms;
  • installs the standard Module Federation compiler plugin;
  • prevents Lynx's local loader from claiming remote-only chunk IDs;
  • encodes the internal container program as a native or Web .lynx.bundle;
  • rewrites manifest metaData.remoteEntry to that public bundle;
  • retains lazy expose bundles as separately deployable HTTP artifacts.
  • pairs each split UI expose's background module with its main-thread snapshot
    program without adding another public container.
  • derives paired chunk suffixes from the DSL's actual main-thread layer; the
    official react:main-thread suffix also prevents Rspeedy from applying its
    background tt wrapper to native main-thread roots.

Standalone iOS shell

The reference iOS application is derived from the official Lynx
ios/HelloLynxSwift UIKit starter at commit
f8230ca6aa1c9e629e30272971d0c03450b13e8e. It embeds LynxView, initializes
LynxEnv, uses the official CocoaPods distribution, and does not depend on
Lynx Explorer.

flowchart LR
  I["OrbitControl.app"] --> V["native LynxView"]
  V --> TF["LynxTemplateResourceFetcher"]
  V --> GF["LynxGenericResourceFetcher"]
  R["Release: embedded host .lynx.bundle"] --> TF
  D["Debug: HTTP host .lynx.bundle"] --> TF
  TF --> M["HTTP(S) mf-manifest.json"]
  M --> C["HTTP(S) container .lynx.bundle"]
  C --> L["HTTP(S) lazy expose .bundle files"]
  GF --> X["external JS/general resources"]
Loading

Release embeds only the host main.lynx.bundle; it does not package the remote
container, lazy exposes, or shared remote fallbacks into the IPA. The host is
built with its production manifest origin before the sync step. Both build
configurations accept LYNX_BUNDLE_URL for Simulator/LAN development. Release
permits only local networking; arbitrary and public insecure HTTP loads remain
disabled.

Physical-device development uses one LYNX_REMOTE_ORIGIN for the host's
compiled manifest URL and LYNX_BUNDLE_URL for the root bundle. The
ios:device command rejects loopback origins and binds Rspeedy to 0.0.0.0,
preventing the common failure where the phone is accidentally directed to its
own localhost.

The shell implements both current resource seams. LynxTemplateResourceFetcher
loads Bundle and Lazy Bundle bytes, while LynxGenericResourceFetcher covers
general/external resources. A legacy LynxTemplateProvider implementation is
retained for SDK compatibility. HTTP failures return status-bearing errors and
all URL-session requests expose cancellation blocks to Lynx.
Local file requests are restricted to the signed app bundle and a shell-owned
temporary cache. Path-based resources preserve extensions and relative bundle
directories, cache the latest path by URL, enforce a 64 MiB aggregate budget
across all immutable retained paths, and remove the cache only with the fetcher so
paths already returned to Lynx remain valid.
Remote responses use delegate-only download tasks with request and resource
timeouts; expected or received bytes above 64 MiB cancel in flight before the
temporary download can grow without bound.

Runtime request path

sequenceDiagram
  participant App as Host import/loadRemote
  participant MF as Federation runtime
  participant Manifest as mf-manifest.json
  participant Lynx as Lynx runtime API
  participant Entry as container.lynx.bundle
  participant Lazy as expose lazy bundle

  App->>MF: import("catalog/Card") or loadRemote("catalog/Card")
  MF->>Manifest: resolve remote metadata
  Manifest-->>MF: type="lynx", remoteEntry URL
  MF->>Lynx: fetchBundle(entry URL)
  Lynx-->>MF: binary bundle
  MF->>Lynx: loadScript(bundle section)
  Lynx-->>MF: Module Federation container
  MF->>Lynx: loadLazyBundle(expose URL)
  Lynx-->>MF: ids + modules + runtime
  MF->>MF: install into calling webpack runtime
  MF-->>App: exposed module factory/exports
Loading

@module-federation/lynx/runtimePlugin handles remoteEntry.type: 'lynx' and .lynx.bundle URLs. Explicit background-only JavaScript entries can use type: 'lynx-js' and lynx.requireModuleAsync. All unrelated remote types return undefined from the hook and continue through their existing loaders.

Entry loads are cached by URL, global name, and realm. Rejected or timed-out promises are evicted, allowing later retries. The default timeout is 30 seconds and is configurable through runtimePluginOptions.timeout.

The lazy-chunk adapter preserves the official ReactLynx synchronous thenable when a cached DynamicComponent and all of its shared consumes install in the same turn. This is required by the first-render lazy() shortcut. Network loads and pending shared consumes use ordinary timeout-bound promises instead.

Manifest contract

Applications configure the manifest, not the generated JavaScript container:

remotes: {
  catalog: 'catalog@https://cdn.example/mf-manifest.json',
}

The manifest remains the standard Module Federation control plane. Its metaData.remoteEntry is rewritten to:

{
  "name": "catalog.native.lynx.bundle",
  "type": "lynx"
}

The JavaScript container emitted by Rspack is an encoder input, not a public remote endpoint. This keeps discovery, versioning, and runtime integration compatible with the existing manifest pipeline while allowing a Lynx-specific transport.

Web remotes use publicPath: 'auto'. Browser-like hosts infer a root-relative manifest base from the fetched Response.url; Node hosts use an absolute manifest URL or an equivalent custom fetch response. The Lynx runtime plugin separately resolves lazy bundle URLs from the resolved container URL because Lynx Web evaluation cannot rely on document.currentScript. Native remotes use an absolute Rspeedy output.assetPrefix from the deployment origin because Lynx's native lazy-bundle bootstrap also consumes Webpack's public path.

Artifact and chunking model

remoteBundle.chunking supports:

Mode Deployment shape Intended use
split (default) Container .lynx.bundle plus independently fetched lazy .bundle files ReactLynx UI, caching, requested-expose delivery
single One native .lynx.bundle containing background-only module chunks Non-UI native modules requiring one artifact

split is the safe federation default. It prevents every expose and shared fallback from being shipped in the entry binary, preserves on-demand transport, and gives each ReactLynx exposure its own paired lazy roots. Native TASM encodes the background module and main-thread snapshot program into the same DynamicComponent .bundle; the enhanced federation container itself remains background-only.

Web remotes reject single because one external bundle has only one main-thread root and cannot activate independently scoped ReactLynx exposure roots. Native single is restricted to background-only modules; ReactLynx UI exposures remain split.

Ordinary Rspeedy entry outputs are preserved by default. Dedicated remote builds can set preserveSourceEntryBundles: false to publish only the federation manifest, container, and lazy bundles.

Layers, issuer layers, and shares

One neutral container carries internal aliases for layer-specific expose implementations; a second container is unnecessary.

flowchart TB
  C["one realm-neutral container"] --> B["BACKGROUND expose aliases"]
  C --> T["MAIN_THREAD expose aliases"]
  B --> BS["realm-qualified background share scope"]
  T --> TS["realm-qualified main-thread share scope"]
  BS --> BI["background singleton instance"]
  TS --> TI["main-thread singleton instance"]
Loading

Split remote bundles and hosts with mainThread: true compile exposes for both issuer layers. Shared declarations are normalized into realm-qualified scopes. Compiler layers and runtime realms are deliberately separate axes: adapterOptions.layer controls Rspack layer/issuerLayer, while share-scope keys remain qualified by the semantic exposed LAYERS.BACKGROUND or LAYERS.MAIN_THREAD realm so runtime routing is stable even with custom compiler layers. The public API uses realm: 'background' | 'main-thread'; application configuration does not depend on private DSL layer names. Background-only hosts and native single remotes initialize only the background scope and reject main-thread shares at configuration time. Native split mode uses the main-thread alias to generate snapshot bytecode inside each lazy bundle, not to launch a second native federation runtime.

shared: {
  'app-state': {
    singleton: true,
    realm: 'background',
  },
}

A singleton is unique within one JavaScript realm, share scope, and share key. The host, compiled imports, runtime API consumers, and remotes can share the same stateful instance in the background realm. The main-thread realm intentionally receives a different instance.

The implementation does not recommend partially sharing ReactLynx internals: @lynx-js/react alone does not cover its /internal, JSX-runtime, Lepus, and lazy-runtime subpaths. Application state and ordinary libraries are safer singleton candidates.

Public API

Host configuration:

plugins: [
  pluginReactLynx(),
  pluginLynxModuleFederation(
    {
      name: 'lynx_host',
      remotes: {
        catalog: 'catalog@https://cdn.example/mf-manifest.json',
      },
      shared: {
        'app-state': { singleton: true, realm: 'background' },
      },
    },
    { environment: 'lynx' },
  ),
]

Compiled import:

const { default: Card } = await import('catalog/Card');

Runtime API:

const federation = createInstance({
  name: 'lynx_runtime_host',
  remotes: [{ name: 'catalog', entry: manifestUrl }],
  plugins: [lynxRuntimePlugin()],
});

const Card = await federation.loadRemote('catalog/Card');

Remote configuration:

pluginLynxModuleFederation(
  {
    name: 'catalog',
    exposes: {
      './Card': './src/Card',
    },
  },
  {
    environment: 'lynx',
    remoteBundle: {
      target: 'lynx',
      filename: 'catalog.native.lynx.bundle',
      chunking: 'split',
      preserveSourceEntryBundles: false,
    },
  },
);

Reference application

apps/lynx-module-federation-demo is an official Rspeedy + ReactLynx application, not a browser-only simulation.

It includes:

  • a standalone signed-capable UIKit project with an app icon, launch screen,
    release/debug ATS split, lifecycle forwarding, and embedded-host sync;
  • native host and remote .lynx.bundle builds;
  • an official pluginQRCode() development path for Lynx Explorer on a phone;
  • a Lynx for Web host mounted through the public <lynx-view> element;
  • three remote screens loaded lazily over HTTP;
  • both compiled import() and runtime loadRemote() consumers;
  • host and remote mutations of one background-realm singleton;
  • independent background and main-thread share scopes.

The Linux native job builds real launchable artifacts, decodes their binary
headers, validates manifest/layer/share metadata, and verifies all HTTP
resources. It uploads the exact host and remote artifacts to a macos-15 job.
That job resolves the locked official Lynx 3.9.0 pods, builds and launches one
Release app on an iPhone Simulator, then runs the federated host, standalone
Catalog, and embedded-host UI scenarios against that binary. It taps Load
remote catalog
, waits for the ready-only native accessibility identifier that
is mounted after compiled imports, runtime loading, and singleton identity all
pass, and asserts requests for the host, manifest, container, and all three lazy
expose bundles. The harness creates and deletes a disposable simulator rather
than changing a developer's existing simulator state. Successful runs retain
the screenshot and request trace; failed runs additionally retain the single
.xcresult bundle. The macOS invocation also runs four XCTest resource-boundary cases in the same Release build. The job caches lockfile-validated Bundler/CocoaPods
material, leaves Xcode DerivedData uncached, disables only the CI-irrelevant
compiler index store, and avoids compiling Lynx twice under Debug and Release.
This matches Lynx upstream's safe cache/build boundary and is native iOS
runtime evidence, not an Explorer or browser proxy.

The Web E2E executes the official runtime in Chromium with a mobile viewport and touch input. It verifies rendering, navigation, both import styles, all HTTP requests, shared-state identity/mutation, and the absence of Lynx/page/console errors.

Compatibility changes

The embedded runtime initializers replace logical assignment syntax (??=, ||=) with equivalent explicit assignments and use Object.prototype.hasOwnProperty.call. This keeps the emitted runtime parseable by ES2019 and Lynx TASM consumers without changing the public runtime API.

The demo currently pins the layers-capable Rspack canary 2.1.5-canary-54a0d8f3-20260715194831. A Node 24 registerHooks shim isolates that canary to Rspeedy's Rspack resolution so the monorepo's normal toolchain is not globally replaced.

The compatibility shim is now one documented, mechanically enforced boundary owned by every demo build/dev/preview script. It can be deleted atomically when Rspeedy accepts the repository Rspack package directly. The final architecture pass also moves remote-bundle state to per-compilation stores, models lazy loading and demo loading as retryable controllers, isolates E2E output/serving, and decomposes the iOS URL, download, storage, and Lynx adapter boundaries.

Failure behavior and safety

  • Missing Lynx runtime APIs fail with explicit errors.
  • Invalid target/filename/layer/manifest combinations fail at configuration time.
  • Colliding normalized expose chunk names are rejected.
  • Remote entry and lazy chunk loads are bounded by timeouts.
  • Failed entry and manifest cache entries are evicted for retry; manifest request identity prevents stale responses from overwriting newer registrations.
  • Bundle URLs come from the configured manifest/public path; no code is fetched from an implicit origin.
  • Release iOS builds permit only local cleartext development origins; public remote origins still require HTTPS.
  • Native file reads are limited to the app bundle and the bounded shell-owned cache.
  • Existing remote types and transport selection remain unchanged; runtime-core
    now evicts rejected entry promises generically so any transport can retry.

Alternatives considered

Two federation containers

Rejected. Layers and issuer layers already select the correct expose and share implementation. Two containers would duplicate negotiation, caching, manifests, and failure states while still not creating cross-thread object identity.

Put all remote code in one .lynx.bundle

Supported only as native background-only single mode. It is not the default because it defeats independent exposure transport, cacheability, and shared-fallback avoidance; it cannot represent independently activated Web main-thread UI roots.

Address remoteEntry.js directly

Rejected. It exposes an intermediate encoding artifact and bypasses the standard manifest discovery contract.

Add Lynx branches throughout runtime-core

Rejected. A runtime plugin owns platform entry loading and chunk registration. Runtime-core remains transport-neutral: it makes failed entry-cache loads retryable, lets preload plugins explicitly abstain, and preserves the fetched manifest URL for standard publicPath: auto inference. No Lynx type or loader is added to core.

Use only runtime loadRemote()

Rejected. Existing application ergonomics depend on compiled federated imports, so both paths are first-class and covered by E2E.

Validation

  • @module-federation/lynx: 121 tests passed across compiler normalization, artifact construction, entry transport, runtime plugins, and native/Web chunk loading.

  • @module-federation/runtime-core: 98 tests passed, including root-relative manifest publicPath: auto inference, stale-registration isolation, and retry after manifest-load failure.

  • @module-federation/sdk: 70 tests passed; 1 skipped.

  • @module-federation/webpack-bundler-runtime: 101 tests passed.

  • Lynx package typecheck, lint, and build passed.

  • Native E2E: host/remote binary decode, manifest/container validation, 3 lazy bundles, and live HTTP serving passed.

  • The artifact E2E decodes a real lazy bundle and verifies the DynamicComponent identity assignment remains inside Rspeedy's tt.define(...exports...) RuntimeWrapper boundary; this guards the iOS-only top-level exports failure found by the simulator.

  • Official iOS pods resolve from the committed lockfile; the CocoaPods-integrated Xcode project parses with both application and UI-test targets.

  • iOS Simulator CI builds a standalone OrbitControl.app, exercises compiled imports and runtime loading, verifies singleton identity, audits every bundle request, and launches the embedded-host Release branch.

  • Lynx for Web E2E: federated Orbit and standalone Catalog executed in real <lynx-view> instances with 66 HTTP requests, including the manifest, container, and three lazy bundles.

  • Frozen lockfile install, Prettier, actionlint, and CI selector validation passed.

  • Final correctness/security and maintainability reviews found and resolved physical-device origin, native file-access, safe-area/rotation, artifact-hermeticity, bounded download, retry-cache, immutable iOS resource-path lifetime, manifest-request lifetime, paired-layer suffix, simulator-state, and Release-coverage issues; the final audit reported no P0-P2 findings.

  • Final GitHub PR matrix on ac671aded: all checks passed, including real Lynx iOS and Metro iOS simulator E2E.

Rollout and follow-ups

  1. Land/publish Rspack layers support and replace the exact canary pin with the released compatible range.
  2. Publish @module-federation/lynx as an opt-in package.
  3. Add signed physical-device execution for LAN routing, production entitlement, and performance coverage.
  4. Validate additional shared libraries and production CDN/cache policies.
  5. Consider exposing transport observability hooks after the initial API settles.

Reviewer guide

  • packages/lynx/src/plugin.ts and pluginOptions.ts: Rspeedy/Rspack integration and normalization boundaries.
  • packages/lynx/src/remoteBundle.ts: layer-aware container and artifact construction.
  • packages/lynx/src/runtimePlugin.ts: remote-entry transport hook.
  • packages/lynx/src/runtimeChunkLoading.ts: lazy-bundle installation into the caller runtime.
  • packages/lynx/src/remoteManifest.ts: manifest rewrite/validation.
  • apps/lynx-module-federation-demo/federation.config.mjs: host/remote reference configuration.
  • apps/lynx-module-federation-demo/ios/: official-starter-derived standalone UIKit shell and locked pods.
  • apps/lynx-module-federation-demo/test/ios/: native Simulator UI/runtime/request E2E.
  • apps/lynx-module-federation-demo/test/: native artifact/HTTP and real Web E2E evidence.
  • .github/workflows/e2e-lynx.yml: CI contract.

Types of changes

  • Docs change / refactoring / dependency upgrade
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Checklist

  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • I have updated the documentation.

@changeset-bot

changeset-bot Bot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ac671ad

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

This PR includes changesets to release 49 packages
Name Type
@module-federation/runtime-core Patch
@module-federation/webpack-bundler-runtime Patch
@module-federation/lynx Minor
@module-federation/sdk Patch
@module-federation/nextjs-mf Patch
@module-federation/runtime Patch
@module-federation/bridge-react Patch
@module-federation/enhanced Patch
@module-federation/esbuild Patch
@module-federation/runtime-tools Patch
lynx-module-federation-demo Patch
@module-federation/devtools Patch
@module-federation/cli Patch
@module-federation/dts-plugin Patch
@module-federation/managers Patch
@module-federation/manifest Patch
@module-federation/metro Patch
@module-federation/modern-js-v3 Patch
@module-federation/modern-js Patch
@module-federation/node Patch
@module-federation/observability-plugin Patch
@module-federation/retry-plugin Patch
@module-federation/rsbuild-plugin Patch
@module-federation/rspack Patch
@module-federation/rspress-plugin Patch
@module-federation/storybook-addon Patch
@module-federation/utilities Patch
@module-federation/bridge-react-webpack-plugin Patch
@module-federation/bridge-vue3 Patch
@module-federation/playground Patch
website-new Patch
shared-tree-shaking-no-server-host Patch
shared-tree-shaking-no-server-provider Patch
@module-federation/inject-external-runtime-core-plugin Patch
@module-federation/metro-plugin-rnc-cli Patch
@module-federation/metro-plugin-rnef Patch
@module-federation/metro-plugin-rock Patch
shared-tree-shaking-with-server-host Patch
shared-tree-shaking-with-server-provider Patch
node-dynamic-remote-new-version Patch
node-dynamic-remote Patch
remote5 Patch
remote6 Patch
@module-federation/third-party-dts-extractor Patch
@module-federation/bridge-shared Patch
@module-federation/error-codes Patch
create-module-federation Patch
@module-federation/treeshake-server Patch
@module-federation/treeshake-frontend 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

@pkg-pr-new

pkg-pr-new Bot commented Jul 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

@module-federation/devtools

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/devtools@ac671ad

@module-federation/cli

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/cli@ac671ad

create-module-federation

pnpm add https://pkg.pr.new/module-federation/core/create-module-federation@ac671ad

@module-federation/dts-plugin

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/dts-plugin@ac671ad

@module-federation/enhanced

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/enhanced@ac671ad

@module-federation/error-codes

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/error-codes@ac671ad

@module-federation/esbuild

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/esbuild@ac671ad

@module-federation/lynx

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/lynx@ac671ad

@module-federation/managers

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/managers@ac671ad

@module-federation/manifest

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/manifest@ac671ad

@module-federation/metro

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/metro@ac671ad

@module-federation/metro-plugin-rnc-cli

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/metro-plugin-rnc-cli@ac671ad

@module-federation/metro-plugin-rnef

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/metro-plugin-rnef@ac671ad

@module-federation/metro-plugin-rock

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/metro-plugin-rock@ac671ad

@module-federation/modern-js

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/modern-js@ac671ad

@module-federation/modern-js-v3

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/modern-js-v3@ac671ad

@module-federation/native-federation-tests

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/native-federation-tests@ac671ad

@module-federation/native-federation-typescript

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/native-federation-typescript@ac671ad

@module-federation/nextjs-mf

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/nextjs-mf@ac671ad

@module-federation/node

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/node@ac671ad

@module-federation/observability-plugin

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/observability-plugin@ac671ad

@module-federation/playground

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/playground@ac671ad

@module-federation/retry-plugin

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/retry-plugin@ac671ad

@module-federation/rsbuild-plugin

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/rsbuild-plugin@ac671ad

@module-federation/rspack

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/rspack@ac671ad

@module-federation/rspress-plugin

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/rspress-plugin@ac671ad

@module-federation/runtime

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/runtime@ac671ad

@module-federation/runtime-core

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/runtime-core@ac671ad

@module-federation/runtime-tools

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/runtime-tools@ac671ad

@module-federation/sdk

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/sdk@ac671ad

@module-federation/storybook-addon

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/storybook-addon@ac671ad

@module-federation/third-party-dts-extractor

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/third-party-dts-extractor@ac671ad

@module-federation/treeshake-frontend

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/treeshake-frontend@ac671ad

@module-federation/treeshake-server

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/treeshake-server@ac671ad

@module-federation/typescript

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/typescript@ac671ad

@module-federation/utilities

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/utilities@ac671ad

@module-federation/webpack-bundler-runtime

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/webpack-bundler-runtime@ac671ad

@module-federation/bridge-react

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/bridge-react@ac671ad

@module-federation/bridge-react-webpack-plugin

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/bridge-react-webpack-plugin@ac671ad

@module-federation/bridge-shared

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/bridge-shared@ac671ad

@module-federation/bridge-vue3

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/bridge-vue3@ac671ad

@module-federation/inject-external-runtime-core-plugin

pnpm add https://pkg.pr.new/module-federation/core/@module-federation/inject-external-runtime-core-plugin@ac671ad

commit: ac671ad

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b6d364925

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/build-and-test.yml
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

🚀 Preview Environment Ready!

Name Status URL
website-new ✅ Active https://nestor-lopez-30320-website-new-core-module-federa-701c886... ↗

Details:

  • Latest Commit: ac671ad
  • Updated at: 7/21/2026, 8:01:29 PM

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Report

16 package(s) changed, 27 unchanged.

Package dist + ESM entry

Package Total dist (raw) Delta ESM gzip Delta
@module-federation/bridge-react 290.4 kB +460 B (+0.2%) 1.3 kB +2 B (+0.2%)
@module-federation/bridge-vue3 181.2 kB +1.5 kB (+0.9%) 26.2 kB +197 B (+0.7%)
@module-federation/dts-plugin 333.2 kB +216 B (+0.1%) 4.7 kB no change
@module-federation/lynx 142.7 kB +142.7 kB (+∞%) 102 B +102 B (+∞%)
@module-federation/native-federation-tests 93.1 kB -72 B (-0.1%) 745 B no change
@module-federation/native-federation-typescript 91.6 kB -72 B (-0.1%) 629 B no change
@module-federation/playground 28.84 MB +6.4 kB (+0.0%) 43.7 kB +26 B (+0.1%)
@module-federation/runtime-core 297.1 kB +3.0 kB (+1.0%) 570 B no change
@module-federation/sdk 129.1 kB +208 B (+0.2%) 785 B no change
@module-federation/webpack-bundler-runtime 67.9 kB +156 B (+0.2%) 531 B no change

Bundle targets

Package Web bundle (gzip) Delta Node bundle (gzip) Delta
@module-federation/bridge-react 12.2 kB -6 B (-0.0%) 13.2 kB -11 B (-0.1%)
@module-federation/bridge-vue3 19.3 kB +153 B (+0.8%) 19.6 kB +143 B (+0.7%)
@module-federation/cli 2.3 kB -5 B (-0.2%) 2.4 kB -30 B (-1.2%)
@module-federation/core 1.0 kB -4 B (-0.4%) 1.0 kB -31 B (-2.9%)
@module-federation/devtools 30.3 kB -4 B (-0.0%) 30.3 kB -24 B (-0.1%)
@module-federation/enhanced 2.7 kB +1 B (+0.0%) 2.8 kB -41 B (-1.4%)
@module-federation/lynx 7.0 kB +7.0 kB (+∞%) 7.0 kB +7.0 kB (+∞%)
@module-federation/metro-plugin-rnc-cli 416 B +1 B (+0.2%) 435 B -24 B (-5.2%)
@module-federation/node 9.2 kB +6 B (+0.1%) 9.2 kB -28 B (-0.3%)
@module-federation/playground 39.5 kB +29 B (+0.1%) 39.5 kB +29 B (+0.1%)
@module-federation/runtime-core 15.5 kB +176 B (+1.1%) 15.3 kB +175 B (+1.1%)
@module-federation/sdk 4.5 kB +16 B (+0.4%) 5.9 kB +18 B (+0.3%)
@module-federation/webpack-bundler-runtime 4.1 kB +5 B (+0.1%) 4.1 kB +5 B (+0.1%)

Tree-shakable entrypoints

Package Export Entry gzip Delta Web bundle (gzip) Delta Node bundle (gzip) Delta Gap (node-web) Delta
@module-federation/webpack-bundler-runtime ./bundler 151 B no change 4.1 kB +5 B (+0.1%) 4.1 kB +5 B (+0.1%) 0 B 0 B

Consumer scenarios

Scenario Web output (gzip) Delta Node output (gzip) Delta Gap (node-web) Delta
Enhanced remoteEntry 21.6 kB +169 B (+0.8%) 23.3 kB +176 B (+0.7%) +1.7 kB +7 B

Total dist (raw): 35.95 MB (+154.6 kB (+0.4%))
Total ESM gzip: 105.2 kB (+327 B (+0.3%))
Total web bundle (gzip): 240.8 kB (+7.4 kB (+3.2%))
Total node bundle (gzip): 243.5 kB (+7.2 kB (+3.0%))
Tracked ./bundler entry gzip: 556 B (no change)
Tracked ./bundler web bundle (gzip): 4.9 kB (+5 B (+0.1%))
Tracked ./bundler node bundle (gzip): 4.9 kB (+5 B (+0.1%))

Bundle sizes are generated with rslib (Rspack). Package-root metrics preserve the historical report. Tracked subpath exports such as ./bundler are measured separately so ENV_TARGET-driven tree-shaking is visible. Bare imports are externalized to keep package-level sizes consistent, and assets are emitted as resources.

@ScriptedAlchemy ScriptedAlchemy changed the title feat(lynx): add native Module Federation support feat(lynx): add production Module Federation support Jul 17, 2026
Comment thread packages/lynx/src/remoteBundle.ts Fixed
Comment thread apps/lynx-module-federation-demo/test/support/artifact-server.mjs Fixed
Comment thread apps/lynx-module-federation-demo/test/support/artifact-server.mjs Fixed
Comment thread apps/lynx-module-federation-demo/test/support/artifact-server.mjs Fixed
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.

2 participants