fix: Ensure federated types are downloaded before type compiling/ type checking

- Adds a function to the webpack.config and documentation to make sure host types are downloaded to the plugin before the first type compile/ type check is happening

Avoids an error where on cold start the types were not yet downloaded but types were already checked.
This commit is contained in:
rvveber 2025-11-12 17:16:11 +01:00
parent 482975c2b5
commit ab74aeff5c
No known key found for this signature in database
GPG key ID: DA468C1089010DD4
5 changed files with 77 additions and 16 deletions

View file

@ -155,13 +155,22 @@ export default MyCustomComponent;
### 4\. Federation Configuration
The core of the plugin is its Webpack configuration. <br>
All plugins should use this sample `webpack.config.js` as a base.
All plugins should use this `webpack.config.js` as a base.
Disclaimer:
We try to not change this file.<br>
But in the future, it may evolve as the plugin system matures.
<br>
```javascript
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const { NativeFederationTypeScriptHost } = require('@module-federation/native-federation-typescript/webpack');
const {
NativeFederationTypeScriptHost,
} = require('@module-federation/native-federation-typescript/webpack');
const {
NativeFederationTypeScriptHost: NativeFederationTypeScriptHostCore,
} = require('@module-federation/native-federation-typescript');
module.exports = (env, argv) => {
const dev = argv.mode !== 'production';
@ -189,6 +198,24 @@ module.exports = (env, argv) => {
},
};
let mfTypesReady;
const ensureFederatedTypesPlugin = {
apply(compiler) {
compiler.hooks.beforeCompile.tapPromise(
'EnsureFederatedTypes',
async () => {
if (!mfTypesReady) {
const downloader = NativeFederationTypeScriptHostCore.raw({
moduleFederationConfig,
});
mfTypesReady = downloader.writeBundle();
}
await mfTypesReady;
},
);
},
};
return {
devServer: {
// The port should match the one in your plugin's configuration file
@ -198,7 +225,12 @@ module.exports = (env, argv) => {
plugins: [
new ModuleFederationPlugin(moduleFederationConfig),
// This plugin enables type-sharing for intellisense
...(dev ? [NativeFederationTypeScriptHost({ moduleFederationConfig })] : []),
...(dev
? [
ensureFederatedTypesPlugin, // ensures the zip is ready before the first compile
NativeFederationTypeScriptHost({ moduleFederationConfig }),
]
: []),
],
// ... other webpack config (output, module rules, etc.)
};
@ -232,8 +264,8 @@ In your plugin's `tsconfig.json`:
<br>
When you run the host application with `NEXT_PUBLIC_DEVELOP_PLUGINS=true`, it generates a `@mf-types.zip` file. <br>
The `NativeFederationTypeScriptHost` plugin in your webpack config will automatically download and unpack it, <br>
making the host's types available to your plugin and IDE.
The `NativeFederationTypeScriptHost` plugin in your webpack config will automatically download and unpack it ahead of the first compile, <br>
making the host's types available to your plugin and IDE. In development with this flag enabled, route changes may be slower because type generation and automatic exposure run during rebuilds; this does not affect production where navigations are instant.
<br>
@ -554,4 +586,4 @@ Update the **`remote.url`** to the public-facing path that matches where you dep
"message": "Hello from production!"
}
}
```
```

View file

@ -443,7 +443,7 @@ const MyCustomComponent: React.FC<ComponentProps> = ({
>
<Loading />
<Text $size="s" style={{ marginTop: '16px', color: '#666' }}>
Loading plugin data...
Fake Loading...
</Text>
</Box>
) : (

View file

@ -153,8 +153,7 @@ const ThemingDemo: React.FC = () => {
color: 'var(--c--theme--colors--greyscale-600)',
lineHeight: '1.4',
}}>
💡 This demo overrides primary-500, primary-600, and secondary-500 tokens.
Any component using these tokens will update!
💡 This demo overrides primary-500, primary-600, and secondary-500 css variables. It only works with CSS.
</div>
</div>
</>

View file

@ -1,6 +1,11 @@
const path = require('path');
const { NativeFederationTypeScriptHost } = require('@module-federation/native-federation-typescript/webpack');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const {
NativeFederationTypeScriptHost,
} = require('@module-federation/native-federation-typescript/webpack');
const {
NativeFederationTypeScriptHost: NativeFederationTypeScriptHostCore,
} = require('@module-federation/native-federation-typescript');
const moduleFederationConfig = {
name: 'plugin_frontend',
@ -31,6 +36,24 @@ const moduleFederationConfig = {
},
};
let mfTypesReady;
const ensureFederatedTypesPlugin = {
apply(compiler) {
compiler.hooks.beforeCompile.tapPromise(
'EnsureFederatedTypes',
async () => {
if (!mfTypesReady) {
const downloader = NativeFederationTypeScriptHostCore.raw({
moduleFederationConfig,
});
mfTypesReady = downloader.writeBundle();
}
await mfTypesReady;
},
);
},
};
module.exports = (env, argv) => {
const dev = argv.mode !== 'production';
@ -70,10 +93,11 @@ module.exports = (env, argv) => {
new ModuleFederationPlugin(moduleFederationConfig),
...(dev
? [
NativeFederationTypeScriptHost({
moduleFederationConfig,
}),
]
ensureFederatedTypesPlugin,
NativeFederationTypeScriptHost({
moduleFederationConfig,
}),
]
: []),
],
};

View file

@ -28,9 +28,15 @@ const nextConfig = {
NEXT_PUBLIC_BUILD_ID: buildId,
},
webpack(config, { isServer, dev }) {
// Prevent rebuild loops by ignoring node_modules and build outputs
// Prevent rebuild loops by ignoring node_modules and generated types/outputs
config.watchOptions = {
ignored: ['**/node_modules/**', '**/.next/**', '**/dist/**'],
ignored: [
'**/node_modules/**',
'**/.next/**',
'**/dist/**',
'**/@mf-types/**',
'**/@mf-types.zip',
],
};
// Grab the existing rule that handles SVG imports