When MF filename contains a subdirectory (e.g., production/remoteEntry.js), the DTS plugin emits @mf-types.zip and @mf-types.d.ts to the compiler output root (dist/), not alongside the entry file (dist/production/).
This is a regression introduced in PR #4230 (f8ff0d8cd), which removed the zipPrefix logic that used path.dirname(config.filename) to correctly place types alongside the entry file.
# 1. Install dependencies
cd remote && npm install
# 2. Build
npx webpack --mode production
# 3. Check output
ls dist/
ls dist/production/dist/
├── production/
│ ├── remoteEntry.js
│ ├── @mf-types.zip ← types alongside entry
│ └── @mf-types.d.ts
├── main.js
└── ...
dist/
├── @mf-types.zip ← WRONG: types at compiler root
├── @mf-types.d.ts ← WRONG
├── production/
│ └── remoteEntry.js ← entry in subdirectory
├── main.js
└── ...
Consumer constructs types URL from entry path:
- Entry:
http://host/dist/production/remoteEntry.js - Expected types:
http://host/dist/production/@mf-types.zip - Actual types:
http://host/dist/@mf-types.zip - → 404
// remote/webpack.config.js
module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist/',
},
plugins: [
new ModuleFederationPlugin({
name: 'remote',
// Entry goes into a subdirectory via filename
filename: 'production/remoteEntry.js',
exposes: { './Button': './src/Button' },
dts: { generateTypes: { generateAPITypes: true } },
}),
],
};In v0.13.1, GenerateTypesPlugin had zipPrefix logic:
if (config.filename) {
zipPrefix = path.dirname(config.filename); // 'production'
}
const zipAssetName = path.join(zipPrefix, zipName); // 'production/@mf-types.zip'PR #4230 (f8ff0d8cd) removed this, so the asset name is now just path.basename(zipTypesPath) = @mf-types.zip, losing the subdirectory.
@module-federation/enhanced: 2.1.0 (bug present)@module-federation/enhanced: 0.13.1 (works correctly)webpack: 5.x