@rollipop/plugin-module-federation 1.0.0-beta.1 → 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +18 -48
  2. package/dist/index.js +2 -2
  3. package/package.json +8 -7
package/README.md CHANGED
@@ -1,28 +1,19 @@
1
1
  # @rollipop/plugin-module-federation
2
2
 
3
- Module Federation for Rollipop. Wires `@module-federation/runtime` into the host bundle and registers each remote's container on `globalThis`, with shared dependencies resolved through a host-owned global registry.
3
+ Share modules and dependencies between React Native bundles.
4
4
 
5
5
  ## Status
6
6
 
7
- > **Initial implementation.** This plugin is an early-stage feature. The public
8
- > API and the bundle layout may change, and the role model is intentionally
9
- > narrower than standard Module Federation — see [Roles](#roles) and
10
- > [Constraints](#constraints).
11
- >
12
- > Notably, a single config takes exactly one role (host **or** remote). Standard
13
- > (web) Module Federation lets one build both `expose` modules and `consume`
14
- > remotes; that bidirectional / chained-federation model is **not supported
15
- > yet**. Lifting it requires emitting the federation container as a separate
16
- > artifact from the app bundle, which is not implemented at this stage.
7
+ > Experimental: the API and bundle layout may change. A config cannot both consume and expose remotes.
17
8
 
18
9
  ## Roles
19
10
 
20
11
  A single Rollipop config takes one role. Defining both `remotes` and `exposes` throws.
21
12
 
22
- | Role | Has | Bundle output |
23
- | ------ | --------- | ------------------------------------------------------------- |
24
- | Host | `remotes` | Normal Rollipop bundle that consumes remotes at runtime. |
25
- | Remote | `exposes` | IIFE for static builds; Rollipop format with HMR when served. |
13
+ | Role | Option | Purpose |
14
+ | ------ | --------- | ------------------------------------------------------ |
15
+ | Host | `remotes` | Loads remote modules and provides shared dependencies. |
16
+ | Remote | `exposes` | Exposes modules to hosts. |
26
17
 
27
18
  ## Host
28
19
 
@@ -36,7 +27,7 @@ import { federation } from '@rollipop/plugin-module-federation';
36
27
  import { defineConfig } from 'rollipop';
37
28
 
38
29
  export default defineConfig({
39
- entry: 'src/host/index.js',
30
+ entry: 'src/host/index.ts',
40
31
  plugins: [
41
32
  federation({
42
33
  name: 'host_app',
@@ -48,8 +39,6 @@ export default defineConfig({
48
39
  'react-native': { singleton: true, eager: true, requiredVersion: '0.84.1' },
49
40
  },
50
41
  runtime: {
51
- // Register a `ModuleFederationScriptLoader` on `globalThis.__rollipop_script_loader__`.
52
- // Typically delegates to a native TurboModule that does fetch + JSI evaluate.
53
42
  implement: `
54
43
  const NativeScriptManager = globalThis.nativeModuleProxy.ScriptManager;
55
44
  globalThis.__rollipop_script_loader__ = {
@@ -74,9 +63,7 @@ const RemoteNavigator = React.lazy(() =>
74
63
  );
75
64
  ```
76
65
 
77
- The imported module's default export is a React component proxy. Rendering it loads the remote
78
- through the federation runtime and suspends while the request is pending, so render it inside a
79
- React `Suspense` boundary. The bundled code does not use native `import()` to fetch the remote.
66
+ Render the imported component inside a React `Suspense` boundary while it loads.
80
67
 
81
68
  Failed remote loads are thrown to the nearest React Error Boundary. From its retry action,
82
69
  reload the remote before resetting the boundary:
@@ -88,8 +75,7 @@ await retryRemote('remote_app/RemoteNavigator');
88
75
  resetErrorBoundary();
89
76
  ```
90
77
 
91
- Concurrent loads share one request. If a retry fails, the promise rejects and the boundary
92
- can keep showing its fallback until the user retries again.
78
+ Pass the import alias and exposed path to `retryRemote`. Failed retries reject.
93
79
 
94
80
  ## Remote
95
81
 
@@ -99,7 +85,7 @@ import { federation } from '@rollipop/plugin-module-federation';
99
85
  import { defineConfig } from 'rollipop';
100
86
 
101
87
  export default defineConfig({
102
- entry: 'src/remote/index.js',
88
+ entry: 'src/remote/index.ts',
103
89
  plugins: [
104
90
  federation({
105
91
  name: 'remote_app',
@@ -115,11 +101,7 @@ export default defineConfig({
115
101
  });
116
102
  ```
117
103
 
118
- Static builds emit an IIFE; the dev server uses Rollipop's format so each remote has its own HMR
119
- runtime. Each shared dependency import (e.g. `import RN from 'react-native'`) is replaced with a
120
- stub that reads from the host's `globalThis.__rollipop_shared__` registry and throws if the
121
- dependency is not registered. Rollipop's prelude and polyfills are skipped because the host has
122
- already initialized the React Native runtime.
104
+ Remotes support HMR when served by the development server.
123
105
 
124
106
  ## Script loader contract
125
107
 
@@ -131,26 +113,14 @@ export interface ModuleFederationScriptLoader {
131
113
  }
132
114
  ```
133
115
 
134
- The host registers an implementation on `globalThis.__rollipop_script_loader__`. The plugin's MF runtime adapter delegates `loadEntry` to it. After the script is evaluated the container is read back from `globalThis[<entryGlobalName>]`.
135
-
136
- ## Globals exposed at runtime
137
-
138
- | Global | Owner | Purpose |
139
- | -------------------------------------- | ------ | ------------------------------------------------------------------------------------ |
140
- | `__rollipop_script_loader__` | user | `ModuleFederationScriptLoader` implementation. |
141
- | `__rollipop_shared__` | host | Shared dependency registry populated during host initialization; missing keys throw. |
142
- | `__rollipop_module_federation_cache__` | plugin | Internal cache of loaded modules, pending requests, and load errors. |
143
-
144
- Use `retryRemote` from `@rollipop/plugin-module-federation/runtime` to retry a failed request.
116
+ Register a loader on `globalThis.__rollipop_script_loader__`. Its `loadScript` method must download and evaluate the bundle before resolving.
145
117
 
146
118
  ## Constraints
147
119
 
148
- - A single config cannot define both `remotes` and `exposes` — this is a
149
- current-implementation limitation, not an inherent Module Federation rule.
150
- Split a host and a remote into two configs and run them as separate Rollipop
151
- processes. See [Status](#status).
152
- - Remote imports expose a default React component proxy. Dynamic `import('<remote>/<expose>')`
153
- returns that proxy, rather than the remote module's namespace directly.
154
- - Subpath imports of shared deps (`react/jsx-dev-runtime`, etc.) are bundled into the remote and consume the parent shared instance through `__rollipop_shared__`.
120
+ - A config cannot define both `remotes` and `exposes`. Run hosts and remotes as separate Rollipop processes.
121
+ - Remote imports support default React component exports.
122
+ - Shared dependency subpaths (such as `react/jsx-dev-runtime`) are bundled separately in each remote.
123
+
124
+ See `examples/module-federation` for a working RN 0.84 host + remote setup with a Kotlin/JNI and Obj-C++ script loader.
155
125
 
156
- See `examples/module-federation` for a working RN 0.84 host + remote setup with a Pure C++/Obj-C++ TurboModule script loader.
126
+ See [Configuration](https://rollipop.dev/docs/features/module-federation#configuration) for remote syntax, shared dependencies, and `shareStrategy`.
package/dist/index.js CHANGED
@@ -361,11 +361,11 @@ function moduleFederationPlugin(config) {
361
361
  load: {
362
362
  filter: [include(id(prefixRegex(VIRTUAL_PREFIX)))],
363
363
  handler(id) {
364
- if (id.startsWith(VIRTUAL_SHARED_SHIM_PREFIX)) return {
364
+ if (id.startsWith("\0rollipop:module-federation:shared:")) return {
365
365
  code: generateSharedShimCode(id.slice(VIRTUAL_SHARED_SHIM_PREFIX.length)),
366
366
  moduleType: "js"
367
367
  };
368
- if (id.startsWith(VIRTUAL_REMOTE_PROXY_PREFIX)) return {
368
+ if (id.startsWith("\0rollipop:module-federation:remote:")) return {
369
369
  code: generateRemoteProxyCode({ remoteId: id.slice(VIRTUAL_REMOTE_PROXY_PREFIX.length) }),
370
370
  moduleType: "js"
371
371
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rollipop/plugin-module-federation",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.2",
4
4
  "homepage": "https://github.com/rollipop-dev/rollipop#readme",
5
5
  "bugs": {
6
6
  "url": "https://github.com/rollipop-dev/rollipop/issues"
@@ -36,21 +36,22 @@
36
36
  "build": "vp pack"
37
37
  },
38
38
  "dependencies": {
39
- "@module-federation/runtime": "2.9.1",
39
+ "@module-federation/runtime": "2.9.2",
40
40
  "dedent": "1.7.2",
41
41
  "es-toolkit": "1.52.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/react": "19.2.14",
45
- "@vitest/coverage-v8": "4.1.11",
45
+ "@vitest/coverage-v8": "5.0.5",
46
46
  "react": "19.2.3",
47
- "rollipop": "1.0.0-beta.1",
47
+ "rollipop": "1.0.0-beta.2",
48
48
  "typescript": "7.0.2",
49
- "vite-plus": "0.3.3",
50
- "vitest": "4.1.11"
49
+ "vite": "@voidzero-dev/vite-plus-core@1.3.0",
50
+ "vite-plus": "1.3.0",
51
+ "vitest": "5.0.5"
51
52
  },
52
53
  "peerDependencies": {
53
- "rollipop": "1.0.0-beta.1"
54
+ "rollipop": "1.0.0-beta.2"
54
55
  },
55
56
  "installConfig": {
56
57
  "hoistingLimits": "none"