Skip to content

Commit d227303

Browse files
authoredFeb 19, 2025··
fix(dts-plugin): optimize generate types in dev (#3450)
1 parent 9062cee commit d227303

File tree

4 files changed

+151
-86
lines changed

4 files changed

+151
-86
lines changed
 

Diff for: ‎.changeset/clever-pianos-cough.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@module-federation/dts-plugin': patch
3+
---
4+
5+
fix(dts-plugin): only block build process in prod env when generating types

Diff for: ‎packages/dts-plugin/src/plugins/ConsumeTypesPlugin.ts

+17-2
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,28 @@ export class ConsumeTypesPlugin implements WebpackPluginInstance {
5757

5858
validateOptions(finalOptions.host);
5959

60-
// only consume once , if remotes update types , DevPlugin will auto sync the latest types
61-
consumeTypes(finalOptions)
60+
const promise = consumeTypes(finalOptions)
6261
.then(() => {
6362
callback();
6463
})
6564
.catch(() => {
6665
callback();
6766
});
67+
68+
compiler.hooks.thisCompilation.tap('mf:generateTypes', (compilation) => {
69+
compilation.hooks.processAssets.tapPromise(
70+
{
71+
name: 'mf:generateTypes',
72+
stage:
73+
// @ts-expect-error use runtime variable in case peer dep not installed , it should execute before generate types
74+
compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER - 1,
75+
},
76+
async () => {
77+
// await consume types promise to make sure the consumer not throw types error
78+
await promise;
79+
},
80+
);
81+
});
82+
// only consume once , if remotes update types , DevPlugin will auto sync the latest types
6883
}
6984
}

Diff for: ‎packages/dts-plugin/src/plugins/GenerateTypesPlugin.ts

+128-66
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Compiler, WebpackPluginInstance } from 'webpack';
1+
import type { Compilation, Compiler, WebpackPluginInstance } from 'webpack';
22
import fs from 'fs';
33
import path from 'path';
44
import { isDev, getCompilerOutputDir } from './utils';
@@ -88,7 +88,130 @@ export class GenerateTypesPlugin implements WebpackPluginInstance {
8888
return fn;
8989
};
9090
const generateTypesFn = getGenerateTypesFn();
91-
let compiledOnce = false;
91+
92+
const emitTypesFiles = async (compilation: Compilation) => {
93+
// Dev types will be generated by DevPlugin, the archive filename usually is dist/.dev-server.zip
94+
try {
95+
const { zipTypesPath, apiTypesPath, zipName, apiFileName } =
96+
retrieveTypesAssetsInfo(finalOptions.remote);
97+
98+
if (isProd && zipName && compilation.getAsset(zipName)) {
99+
callback();
100+
return;
101+
}
102+
103+
await generateTypesFn(finalOptions);
104+
const config = finalOptions.remote.moduleFederationConfig;
105+
let zipPrefix = '';
106+
if (typeof config.manifest === 'object' && config.manifest.filePath) {
107+
zipPrefix = config.manifest.filePath;
108+
} else if (
109+
typeof config.manifest === 'object' &&
110+
config.manifest.fileName
111+
) {
112+
zipPrefix = path.dirname(config.manifest.fileName);
113+
} else if (config.filename) {
114+
zipPrefix = path.dirname(config.filename);
115+
}
116+
117+
if (isProd) {
118+
const zipAssetName = path.join(zipPrefix, zipName);
119+
const apiAssetName = path.join(zipPrefix, apiFileName);
120+
if (zipTypesPath && !compilation.getAsset(zipAssetName)) {
121+
compilation.emitAsset(
122+
zipAssetName,
123+
new compiler.webpack.sources.RawSource(
124+
fs.readFileSync(zipTypesPath),
125+
false,
126+
),
127+
);
128+
}
129+
130+
if (apiTypesPath && !compilation.getAsset(apiAssetName)) {
131+
compilation.emitAsset(
132+
apiAssetName,
133+
new compiler.webpack.sources.RawSource(
134+
fs.readFileSync(apiTypesPath),
135+
false,
136+
),
137+
);
138+
}
139+
callback();
140+
} else {
141+
const isEEXIST = (err: NodeJS.ErrnoException) => {
142+
return err.code == 'EEXIST';
143+
};
144+
if (zipTypesPath) {
145+
const zipContent = fs.readFileSync(zipTypesPath);
146+
const zipOutputPath = path.join(
147+
compiler.outputPath,
148+
zipPrefix,
149+
zipName,
150+
);
151+
await new Promise<void>((resolve, reject) => {
152+
compiler.outputFileSystem.mkdir(
153+
path.dirname(zipOutputPath),
154+
(err) => {
155+
if (err && !isEEXIST(err)) {
156+
reject(err);
157+
} else {
158+
compiler.outputFileSystem.writeFile(
159+
zipOutputPath,
160+
zipContent,
161+
(writeErr) => {
162+
if (writeErr && !isEEXIST(writeErr)) {
163+
reject(writeErr);
164+
} else {
165+
resolve();
166+
}
167+
},
168+
);
169+
}
170+
},
171+
);
172+
});
173+
}
174+
175+
if (apiTypesPath) {
176+
const apiContent = fs.readFileSync(apiTypesPath);
177+
const apiOutputPath = path.join(
178+
compiler.outputPath,
179+
zipPrefix,
180+
apiFileName,
181+
);
182+
await new Promise<void>((resolve, reject) => {
183+
compiler.outputFileSystem.mkdir(
184+
path.dirname(apiOutputPath),
185+
(err) => {
186+
if (err && !isEEXIST(err)) {
187+
reject(err);
188+
} else {
189+
compiler.outputFileSystem.writeFile(
190+
apiOutputPath,
191+
apiContent,
192+
(writeErr) => {
193+
if (writeErr && !isEEXIST(writeErr)) {
194+
reject(writeErr);
195+
} else {
196+
resolve();
197+
}
198+
},
199+
);
200+
}
201+
},
202+
);
203+
});
204+
}
205+
206+
callback();
207+
}
208+
} catch (err) {
209+
callback();
210+
if (finalOptions.displayErrorInTerminal) {
211+
console.error('Error in mf:generateTypes processAssets hook:', err);
212+
}
213+
}
214+
};
92215

93216
compiler.hooks.thisCompilation.tap('mf:generateTypes', (compilation) => {
94217
compilation.hooks.processAssets.tapPromise(
@@ -100,70 +223,9 @@ export class GenerateTypesPlugin implements WebpackPluginInstance {
100223
},
101224
async () => {
102225
await consumeTypesPromise;
103-
try {
104-
if (pluginOptions.dev === false && compiledOnce) {
105-
return;
106-
}
107-
108-
if (compiledOnce) {
109-
// Dev types will be generated by DevPlugin, the archive filename usually is dist/.dev-server.zip
110-
return;
111-
}
112-
113-
const { zipTypesPath, apiTypesPath, zipName, apiFileName } =
114-
retrieveTypesAssetsInfo(finalOptions.remote);
115-
if (zipName && compilation.getAsset(zipName)) {
116-
return;
117-
}
118-
119-
await generateTypesFn(finalOptions);
120-
const config = finalOptions.remote.moduleFederationConfig;
121-
let zipPrefix = '';
122-
if (
123-
typeof config.manifest === 'object' &&
124-
config.manifest.filePath
125-
) {
126-
zipPrefix = config.manifest.filePath;
127-
} else if (
128-
typeof config.manifest === 'object' &&
129-
config.manifest.fileName
130-
) {
131-
zipPrefix = path.dirname(config.manifest.fileName);
132-
} else if (config.filename) {
133-
zipPrefix = path.dirname(config.filename);
134-
}
135-
136-
const zipAssetName = path.join(zipPrefix, zipName);
137-
if (zipTypesPath && !compilation.getAsset(zipAssetName)) {
138-
compilation.emitAsset(
139-
path.join(zipPrefix, zipName),
140-
new compiler.webpack.sources.RawSource(
141-
fs.readFileSync(zipTypesPath),
142-
false,
143-
),
144-
);
145-
}
146-
147-
const apiAssetName = path.join(zipPrefix, apiFileName);
148-
if (apiTypesPath && !compilation.getAsset(apiAssetName)) {
149-
compilation.emitAsset(
150-
path.join(zipPrefix, apiFileName),
151-
new compiler.webpack.sources.RawSource(
152-
fs.readFileSync(apiTypesPath),
153-
false,
154-
),
155-
);
156-
}
157-
compiledOnce = true;
158-
callback();
159-
} catch (err) {
160-
callback();
161-
if (finalOptions.displayErrorInTerminal) {
162-
console.error(
163-
'Error in mf:generateTypes processAssets hook:',
164-
err,
165-
);
166-
}
226+
const emitTypesFilesPromise = emitTypesFiles(compilation);
227+
if (isProd) {
228+
await emitTypesFilesPromise;
167229
}
168230
},
169231
);

Diff for: ‎packages/manifest/src/utils.ts

+1-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Chunk, Compilation, StatsCompilation, StatsModule } from 'webpack';
22
import path from 'path';
3+
import fs from 'fs';
34
import {
45
StatsAssets,
56
moduleFederationPlugin,
@@ -307,24 +308,6 @@ export function getTypesMetaInfo(
307308
const zip = path.join(zipPrefix, zipName);
308309
const api = path.join(zipPrefix, apiFileName);
309310

310-
if (!zip || !compilation.getAsset(zip)) {
311-
return {
312-
path: '',
313-
name: '',
314-
zip: '',
315-
api: '',
316-
};
317-
}
318-
319-
if (!api || !compilation.getAsset(api)) {
320-
return {
321-
path: '',
322-
name: '',
323-
zip,
324-
api: '',
325-
};
326-
}
327-
328311
return {
329312
path: '',
330313
name: '',

0 commit comments

Comments
 (0)
Please sign in to comment.