Skip to content

Commit f65eb2d

Browse files
authored
docs: add detailed error handling for cross-origin scripts (module-federation#4569)
1 parent ec5d899 commit f65eb2d

2 files changed

Lines changed: 191 additions & 0 deletions

File tree

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

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import ErrorCodeTitle from '@components/ErrorCodeTitle';
22
import EnableAsyncEntry from '@components/en/EnableAsyncEntry';
33
import Runtime from '@components/en/runtime/index';
4+
import { Tab, Tabs } from '@theme';
45

56
# Runtime
67

@@ -45,6 +46,97 @@ There are corresponding solutions for the reasons:
4546
4. Loading a ESM remote from a non-esm host
4647
- setting `type: 'module'` on the remote config
4748

49+
### Getting More Detailed Error Information
50+
51+
If you have confirmed that `window[remoteEntryKey]` does not exist (i.e. the script failed to register the container), but there is no clear error message in the console, it may be because the producer script is a cross-origin resource. Browsers hide error details for cross-origin scripts by default (`filename`, `lineno`, and `message` are all empty).
52+
53+
You can add a `crossorigin` attribute to the script via the [`createScript`](../../plugin/dev/index#createscript) hook, provided that the producer server has `Access-Control-Allow-Origin` configured.
54+
55+
First, create the runtime plugin file:
56+
57+
```ts title="script-crossorigin-plugin.ts"
58+
import { ModuleFederationRuntimePlugin } from '@module-federation/runtime/types';
59+
60+
export default function MFScriptPlugin(): ModuleFederationRuntimePlugin {
61+
return {
62+
name: 'script-crossorigin-plugin',
63+
createScript({ url }) {
64+
const script = document.createElement('script');
65+
script.src = url;
66+
script.crossOrigin = 'anonymous';
67+
return script;
68+
},
69+
// If preloadRemote is used in your project, also implement createLink to keep
70+
// crossorigin consistent. Without this, the preload (no-cors) and actual load
71+
// (cors) will have different cache keys, causing the preload to be wasted.
72+
createLink({ url, attrs }) {
73+
const link = document.createElement('link');
74+
link.setAttribute('href', url);
75+
link.setAttribute('crossorigin', 'anonymous');
76+
if (attrs) {
77+
Object.entries(attrs).forEach(([key, value]) => {
78+
if (key !== 'crossorigin') {
79+
link.setAttribute(key, String(value));
80+
}
81+
});
82+
}
83+
return link;
84+
},
85+
};
86+
}
87+
```
88+
89+
Then register the plugin:
90+
91+
<Tabs>
92+
<Tab label="Build Plugin">
93+
94+
Register via the `runtimePlugins` field in your build config:
95+
96+
```ts title="rspack.config.ts"
97+
const path = require('path');
98+
module.exports = {
99+
plugins: [
100+
new ModuleFederationPlugin({
101+
name: 'host',
102+
remotes: { /* ... */ },
103+
runtimePlugins: [
104+
path.resolve(__dirname, './script-crossorigin-plugin.ts'),
105+
],
106+
}),
107+
],
108+
};
109+
```
110+
111+
</Tab>
112+
<Tab label="Runtime API">
113+
114+
Register via the `plugins` field in the `init` API:
115+
116+
```ts title="index.ts"
117+
import { init } from '@module-federation/runtime';
118+
import MFScriptPlugin from './script-crossorigin-plugin';
119+
120+
init({
121+
name: 'host',
122+
remotes: [/* ... */],
123+
plugins: [MFScriptPlugin()],
124+
});
125+
```
126+
127+
</Tab>
128+
</Tabs>
129+
130+
Once added, runtime exceptions thrown by cross-origin scripts will include the full `filename`, `lineno`, and `message`, making it easier to locate the specific error.
131+
132+
:::tip
133+
134+
* If the server does not have CORS headers configured, adding the `crossorigin` attribute will instead cause the script to fail to load (network error). Confirm the server is correctly configured before using this approach.
135+
136+
* MF does not sync this automatically because doing so would require calling the `createScript` hook speculatively during preload to read the `crossOrigin` value — hook implementations may have side effects (e.g. registering listeners, mutating global state), so invoking the hook twice could cause unpredictable behavior.
137+
138+
:::
139+
48140
## RUNTIME-002
49141

50142
<ErrorCodeTitle code='RUNTIME-002'/>
@@ -182,6 +274,10 @@ The script file downloaded successfully, but its IIFE threw a runtime exception
182274
A `ScriptExecutionError` means the script was downloaded successfully — retrying will not fix this type of error. Focus on investigating compatibility or runtime errors in the producer's code first.
183275
:::
184276

277+
### Getting More Detailed Error Information
278+
279+
Prior to **v2.2.0**, RUNTIME-008 error messages may not include specific exception details. If the error message is not descriptive enough, refer to 「[RUNTIME-001 Getting More Detailed Error Information](#getting-more-detailed-error-information)」 for how to add the `crossorigin` attribute to retrieve the full error stack.
280+
185281
## RUNTIME-009
186282

187283
<ErrorCodeTitle code='RUNTIME-009'/>

apps/website-new/docs/zh/guide/troubleshooting/runtime.mdx

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import ErrorCodeTitle from '@components/ErrorCodeTitle';
22
import EnableAsyncEntry from '@components/zh/EnableAsyncEntry';
33
import Runtime from '@components/zh/runtime/index';
4+
import { Tab, Tabs } from '@theme';
45

56
# 运行时
67

@@ -42,6 +43,96 @@ import Runtime from '@components/zh/runtime/index';
4243
2. 若项目构建器为 rspack ,查看最终构建配置是否设置了 [runtimeChunk](https://rspack.dev/config/optimization#optimizationruntimechunk) ,若是则删除此配置
4344
3. 检查该资源是否是外部可访问
4445

46+
### 获取更详细的错误信息
47+
48+
如果已确认 `window[remoteEntryKey]` 不存在(即脚本未能正常挂载容器),但控制台没有明确的报错信息,可能是因为生产者脚本为跨域资源,浏览器出于安全限制会隐藏跨域脚本的错误详情(`filename``lineno``message` 均为空)。
49+
50+
此时可通过 [`createScript`](../../plugin/dev/index#createscript) hook 为脚本添加 `crossorigin` 属性,前提是生产者服务端已配置 `Access-Control-Allow-Origin` 响应头。
51+
52+
首先创建运行时插件文件:
53+
54+
```ts title="script-crossorigin-plugin.ts"
55+
import { ModuleFederationRuntimePlugin } from '@module-federation/runtime/types';
56+
57+
export default function MFScriptPlugin(): ModuleFederationRuntimePlugin {
58+
return {
59+
name: 'script-crossorigin-plugin',
60+
createScript({ url }) {
61+
const script = document.createElement('script');
62+
script.src = url;
63+
script.crossOrigin = 'anonymous';
64+
return script;
65+
},
66+
// 若项目中使用了 preloadRemote,需同步设置 createLink 的 crossorigin,
67+
// 否则预加载(no-cors)与实际加载(cors)缓存键不匹配,导致预加载失效。
68+
createLink({ url, attrs }) {
69+
const link = document.createElement('link');
70+
link.setAttribute('href', url);
71+
link.setAttribute('crossorigin', 'anonymous');
72+
if (attrs) {
73+
Object.entries(attrs).forEach(([key, value]) => {
74+
if (key !== 'crossorigin') {
75+
link.setAttribute(key, String(value));
76+
}
77+
});
78+
}
79+
return link;
80+
},
81+
};
82+
}
83+
```
84+
85+
然后注册该插件:
86+
87+
<Tabs>
88+
<Tab label="构建插件注册">
89+
90+
通过构建配置中的 `runtimePlugins` 字段注册:
91+
92+
```ts title="rspack.config.ts"
93+
const path = require('path');
94+
module.exports = {
95+
plugins: [
96+
new ModuleFederationPlugin({
97+
name: 'host',
98+
remotes: { /* ... */ },
99+
runtimePlugins: [
100+
path.resolve(__dirname, './script-crossorigin-plugin.ts'),
101+
],
102+
}),
103+
],
104+
};
105+
```
106+
107+
</Tab>
108+
<Tab label="Runtime API 注册">
109+
110+
通过 `init` API 的 `plugins` 字段注册:
111+
112+
```ts title="index.ts"
113+
import { init } from '@module-federation/runtime';
114+
import MFScriptPlugin from './script-crossorigin-plugin';
115+
116+
init({
117+
name: 'host',
118+
remotes: [/* ... */],
119+
plugins: [MFScriptPlugin()],
120+
});
121+
```
122+
123+
</Tab>
124+
</Tabs>
125+
126+
添加后,跨域脚本执行时抛出的异常将携带完整的 `filename``lineno``message` 信息,便于定位具体报错位置。
127+
128+
:::tip
129+
130+
* 若服务端未配置 CORS 响应头,添加 `crossorigin` 属性反而会导致脚本加载失败(网络错误)。请先确认服务端已正确配置后再使用此方法。
131+
132+
* MF 未自动设置该属性,原因是:在预加载阶段若自动调用 `createScript` hook 来探测 `crossOrigin` 值,而 hook 内部可能存在注册监听器、修改全局状态等副作用,被调用两次会引发不可预期的问题。
133+
134+
:::
135+
45136
## RUNTIME-002
46137

47138
<ErrorCodeTitle code='RUNTIME-002'/>
@@ -181,6 +272,10 @@ import Runtime from '@components/zh/runtime/index';
181272
ScriptExecutionError 表示脚本已成功下载,重试不会解决此类问题。需优先排查生产者代码本身的兼容性或运行时错误。
182273
:::
183274

275+
### 获取更详细的错误信息
276+
277+
**v2.2.0 之前**,RUNTIME-008 的错误信息可能不包含具体的异常详情。若遇到错误信息不够明确的情况,可参考 「[RUNTIME-001 获取更详细的错误信息](#获取更详细的错误信息)」 中的方法,通过添加 `crossorigin` 属性来获取完整的错误堆栈。
278+
184279
## RUNTIME-009
185280

186281
<ErrorCodeTitle code='RUNTIME-009'/>

0 commit comments

Comments
 (0)