Skip to content

Commit 806abf2

Browse files
Merge branch 'pr8-unified-api' into pr9-implementation
Resolved conflict in strip-claude-coauthor.sh by keeping the more robust implementation from pr9-implementation that checks both email AND name for author verification, while maintaining the detailed logging and safety features.
2 parents a6a1ba6 + eb887c2 commit 806abf2

File tree

12 files changed

+1665
-90
lines changed

12 files changed

+1665
-90
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,4 @@ vitest.config.*.timestamp*
8888
.rsbuild
8989
ssg
9090
.claude
91+
__mocks__/

apps/website-new/docs/en/configure/shareStrategy.mdx

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,48 @@
22

33
- Type: `'version-first' | 'loaded-first'`
44
- Required: No
5-
- Default: `'version-first'`
5+
- Default: `'version-first'` (set by webpack plugin/bundler runtime)
66

77
Control the loading strategy of shared dependencies:
88

9-
- `'version-first'`: Version priority, ensuring that the highest version of shared dependencies is used. After setting, all *remotes* entry files will be automatically loaded and **register** the corresponding shared dependencies to ensure that all shared dependency versions can be obtained. This strategy is recommended when there are strict version requirements.
9+
- `'version-first'`: Version priority, ensuring that the highest version of shared dependencies is used. **All remotes entry files will be automatically loaded during initialization** to register shared dependencies and ensure version compatibility. This strategy is recommended when there are strict version requirements.
1010

11-
- `'loaded-first'`: Prioritize reuse, greatly reducing redundant dependency requests. After setting, the *remotes* entry file will not be automatically loaded (it will only be loaded when needed), and the loaded shared dependencies will be reused first. This strategy is recommended when there are no strict requirements on the version and performance is required.
11+
- `'loaded-first'`: Prioritize reuse of already loaded shared dependencies. Remotes are loaded on-demand rather than during initialization. This strategy is recommended when you want to reuse loaded dependencies for performance.
12+
13+
:::warning Offline Remote Considerations
14+
15+
The `'version-first'` strategy automatically loads remote entry files during application startup to initialize shared dependencies. If any remote is offline or unreachable, this will trigger the `errorLoadRemote` hook with `lifecycle: 'beforeLoadShare'` during startup. Without proper error handling, this can cause application initialization failures.
16+
17+
The `'loaded-first'` strategy defers remote loading until modules are actually requested, which means offline remotes only cause failures when those specific remotes are accessed, not during application startup.
18+
19+
**What happens with version-first and offline remotes:**
20+
1. During initialization, remotes are loaded via `initializeSharing()`
21+
2. If remote manifest/entry is offline, `module.getEntry()` fails
22+
3. `errorLoadRemote` hook is called with `lifecycle: 'beforeLoadShare'`
23+
4. If no fallback is provided, the initialization may hang or fail
24+
25+
**Required error handling for version-first:**
26+
```ts
27+
const plugin = {
28+
name: 'offline-resilient-plugin',
29+
errorLoadRemote(args) {
30+
if (args.lifecycle === 'beforeLoadShare') {
31+
// Handle version-first offline remote during startup
32+
return {
33+
init: () => Promise.resolve(),
34+
get: (module) => () => Promise.resolve(() => 'Offline Fallback')
35+
};
36+
}
37+
}
38+
};
39+
```
40+
41+
For production applications with potential network issues, consider:
42+
- Using `'loaded-first'` strategy for better resilience
43+
- Implementing comprehensive error handling for `'beforeLoadShare'` lifecycle
44+
- Adding retry mechanisms via plugins
45+
- Using proper error boundaries and fallback components
46+
47+
See [Error Load Remote Solutions](../blog/error-load-remote) for detailed offline handling strategies.
48+
49+
:::

apps/website-new/docs/en/guide/basic/runtime/runtime-hooks.mdx

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -159,29 +159,80 @@ async function errorLoadRemote(args: ErrorLoadRemoteOptions): Promise<void | unk
159159
type ErrorLoadRemoteOptions ={
160160
id: string;
161161
error: unknown;
162+
options?: any;
162163
from: 'build' | 'runtime';
164+
lifecycle: 'beforeRequest' | 'beforeLoadShare' | 'afterResolve' | 'onLoad';
163165
origin: ModuleFederation;
164166
}
165167
```
168+
169+
The `lifecycle` parameter indicates the stage where the error occurred:
170+
171+
- `beforeRequest`: Error during initial request processing
172+
- `afterResolve`: Error during manifest loading (most common for network failures)
173+
- `onLoad`: Error during module loading and execution
174+
- `beforeLoadShare`: Error during shared dependency loading
175+
166176
* example
167177
168178
```ts
169179
import { createInstance, loadRemote } from '@module-federation/enhanced/runtime'
170-
171180
import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime';
172181

173182
const fallbackPlugin: () => ModuleFederationRuntimePlugin =
174183
function () {
175184
return {
176185
name: 'fallback-plugin',
177186
errorLoadRemote(args) {
178-
const fallback = 'fallback'
179-
return fallback;
187+
const { lifecycle, id, error } = args;
188+
189+
// Log error details safely
190+
if (error) {
191+
console.warn(`Failed to load remote ${id} at ${lifecycle}:`, error?.message || error);
192+
}
193+
194+
// Handle different error types based on lifecycle
195+
switch (lifecycle) {
196+
case 'afterResolve':
197+
// Manifest loading failed - provide fallback manifest or alternative URL
198+
return {
199+
id: id || 'fallback',
200+
name: id || 'fallback',
201+
metaData: { /* fallback manifest */ },
202+
shared: [],
203+
remotes: [],
204+
exposes: []
205+
};
206+
207+
case 'beforeRequest':
208+
// Request processing failed - can return modified args or void
209+
console.warn(`Request processing failed for ${id}`);
210+
return void 0;
211+
212+
case 'onLoad':
213+
// Module loading failed - provide fallback component
214+
return () => ({
215+
__esModule: true,
216+
default: () => 'Fallback Component'
217+
});
218+
219+
case 'beforeLoadShare':
220+
// Shared dependency loading failed - return fallback shared module
221+
console.warn(`Shared dependency loading failed for ${id}`);
222+
return () => ({
223+
__esModule: true,
224+
default: {}
225+
});
226+
227+
default:
228+
// Unknown lifecycle - log and return void
229+
console.warn(`Unknown lifecycle ${lifecycle} for ${id}`);
230+
return void 0;
231+
}
180232
},
181233
};
182234
};
183235

184-
185236
const mf = createInstance({
186237
name: 'mf_host',
187238
remotes: [
@@ -196,7 +247,7 @@ const mf = createInstance({
196247

197248
mf.loadRemote('app1/un-existed-module').then(mod=>{
198249
expect(mod).toEqual('fallback');
199-
})
250+
});
200251
```
201252

202253
## beforeLoadShare
@@ -242,7 +293,7 @@ type ResolveShareOptions ={
242293
* example
243294
244295
```ts
245-
import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime'
296+
import { createInstance, loadRemote } from '@module-federation/enhanced/runtime'
246297

247298
import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime';
248299

@@ -337,8 +388,6 @@ interface PreloadAssets {
337388
}
338389
```
339390

340-
## loaderHook
341-
342391
## createScript
343392

344393
`SyncHook`
@@ -365,9 +414,9 @@ const changeScriptAttributePlugin: () => ModuleFederationRuntimePlugin =
365414
return {
366415
name: 'change-script-attribute',
367416
createScript({ url }) {
368-
if (url === testRemoteEntry) {
417+
if (url === 'http://localhost:3001/remoteEntry.js') {
369418
let script = document.createElement('script');
370-
script.src = testRemoteEntry;
419+
script.src = url;
371420
script.setAttribute('loader-hooks', 'isTrue');
372421
script.setAttribute('crossorigin', 'anonymous');
373422
return script;

apps/website-new/docs/en/guide/troubleshooting/runtime/RUNTIME-003.mdx

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,94 @@ import ErrorCodeTitle from '@components/ErrorCodeTitle';
44

55
## Reasons
66

7-
Failed to load the manifest, the manifest url may be wrong
7+
Failed to load the manifest, which can occur due to:
8+
9+
1. The manifest URL may be wrong or unreachable
10+
2. Network connectivity issues
11+
3. CORS (Cross-Origin Resource Sharing) restrictions
12+
4. Remote service is offline or not yet deployed
13+
5. DNS resolution failures
814

915
## Solutions
1016

11-
1. Check whether manifestUrl can be accessed normally alone
12-
2. Check manifestUrl for cross-domain issues
17+
### Basic Troubleshooting
18+
1. Check whether manifestUrl can be accessed directly in browser
19+
2. Verify the manifest URL format and path correctness
20+
3. Check network connectivity and DNS resolution
21+
4. Review CORS configuration on the remote server
22+
23+
### Advanced Solutions
24+
25+
#### 1. Implement Error Handling with Runtime Plugins
26+
27+
Use the `errorLoadRemote` hook to handle manifest loading failures gracefully:
28+
29+
```ts
30+
import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime';
31+
32+
const offlineHandlingPlugin: () => ModuleFederationRuntimePlugin = () => ({
33+
name: 'offline-handling-plugin',
34+
errorLoadRemote(args) {
35+
const { lifecycle, id, error } = args;
36+
37+
// Handle different error scenarios based on lifecycle stage
38+
if (lifecycle === 'afterResolve') {
39+
// Manifest loading failure during normal remote loading
40+
console.warn(`Failed to load manifest for remote ${id}:`, error);
41+
// Provide fallback manifest or backup URL
42+
return {
43+
id: 'fallback',
44+
name: 'fallback',
45+
metaData: { /* fallback configuration */ },
46+
shared: [],
47+
remotes: [],
48+
exposes: []
49+
};
50+
}
51+
52+
if (lifecycle === 'beforeLoadShare') {
53+
// Remote loading failure during version-first initialization
54+
console.warn(`Remote ${id} is offline during startup (version-first):`, error);
55+
// Return mock RemoteEntryExports to allow app to continue
56+
return {
57+
init: () => Promise.resolve(),
58+
get: (moduleName) => () => Promise.resolve(() => 'Fallback Component')
59+
};
60+
}
61+
},
62+
});
63+
```
64+
65+
#### 2. Use Retry Plugin
66+
67+
Add automatic retry mechanism for transient network failures:
68+
69+
```ts
70+
import { RetryPlugin } from '@module-federation/retry-plugin';
71+
72+
// In your Module Federation configuration
73+
runtimePlugins: [
74+
() => RetryPlugin({
75+
fetch: {
76+
retryTimes: 3,
77+
retryDelay: 1000,
78+
},
79+
}),
80+
],
81+
```
82+
83+
#### 3. Environment-Specific Handling
84+
85+
For different environments, consider:
86+
- **Development**: Log detailed error information
87+
- **Production**: Provide graceful fallbacks without exposing internal errors
88+
- **Staging**: Use backup servers or cached manifests
89+
90+
### ShareStrategy Impact
91+
92+
The shareStrategy affects when manifest loading occurs:
93+
94+
- `shareStrategy: 'version-first'` - Triggers manifest loading during application initialization for all remotes. Offline remotes will cause startup failures.
95+
- `shareStrategy: 'loaded-first'` - Defers manifest loading until remotes are actually used. Offline remotes only cause failures when accessing those specific remotes.
96+
97+
For resilience against offline remotes, consider using `'loaded-first'` strategy combined with proper error handling.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
["index", "retry-plugin"]
1+
["index", "retry-plugin", "building-custom-retry-plugin"]

0 commit comments

Comments
 (0)