From b1f015373193b29d046dd851601c2e27d0e1eb5a Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Tue, 21 Jan 2025 17:40:45 +0800 Subject: [PATCH] perf(dts-plugin): only block build process in prod env when generating types --- .changeset/clever-pianos-cough.md | 5 + .../src/plugins/GenerateTypesPlugin.ts | 204 ++++++++++++------ 2 files changed, 142 insertions(+), 67 deletions(-) create mode 100644 .changeset/clever-pianos-cough.md diff --git a/.changeset/clever-pianos-cough.md b/.changeset/clever-pianos-cough.md new file mode 100644 index 0000000000..f5921f2e7a --- /dev/null +++ b/.changeset/clever-pianos-cough.md @@ -0,0 +1,5 @@ +--- +'@module-federation/dts-plugin': patch +--- + +perf(dts-plugin): only block build process in prod env when generating types diff --git a/packages/dts-plugin/src/plugins/GenerateTypesPlugin.ts b/packages/dts-plugin/src/plugins/GenerateTypesPlugin.ts index d617cf06b1..1b96954510 100644 --- a/packages/dts-plugin/src/plugins/GenerateTypesPlugin.ts +++ b/packages/dts-plugin/src/plugins/GenerateTypesPlugin.ts @@ -1,4 +1,4 @@ -import type { Compiler, WebpackPluginInstance } from 'webpack'; +import type { Compilation, Compiler, WebpackPluginInstance } from 'webpack'; import fs from 'fs'; import path from 'path'; import { isDev, getCompilerOutputDir } from './utils'; @@ -88,7 +88,139 @@ export class GenerateTypesPlugin implements WebpackPluginInstance { return fn; }; const generateTypesFn = getGenerateTypesFn(); - let compiledOnce = false; + + const emitTypesFiles = async (compilation: Compilation) => { + // Dev types will be generated by DevPlugin, the archive filename usually is dist/.dev-server.zip + try { + await consumeTypesPromise; + + const { zipTypesPath, apiTypesPath, zipName, apiFileName } = + retrieveTypesAssetsInfo(finalOptions.remote); + + if (isProd && zipName && compilation.getAsset(zipName)) { + return; + } + + await generateTypesFn(finalOptions); + const config = finalOptions.remote.moduleFederationConfig; + let zipPrefix = ''; + if (typeof config.manifest === 'object' && config.manifest.filePath) { + zipPrefix = config.manifest.filePath; + } else if ( + typeof config.manifest === 'object' && + config.manifest.fileName + ) { + zipPrefix = path.dirname(config.manifest.fileName); + } else if (config.filename) { + zipPrefix = path.dirname(config.filename); + } + + if (isProd) { + const zipAssetName = path.join(zipPrefix, zipName); + const apiAssetName = path.join(zipPrefix, apiFileName); + if (zipTypesPath && !compilation.getAsset(zipAssetName)) { + compilation.emitAsset( + path.join(zipPrefix, zipName), + new compiler.webpack.sources.RawSource( + fs.readFileSync(zipTypesPath), + false, + ), + ); + } + + if (apiTypesPath && !compilation.getAsset(apiAssetName)) { + compilation.emitAsset( + path.join(zipPrefix, apiFileName), + new compiler.webpack.sources.RawSource( + fs.readFileSync(apiTypesPath), + false, + ), + ); + } + callback(); + } else { + const isEEXIST = (err: NodeJS.ErrnoException) => { + return err.code == 'EEXIST'; + }; + if (zipTypesPath) { + const zipContent = fs.readFileSync(zipTypesPath); + const zipOutputPath = path.join( + compiler.outputPath, + zipPrefix, + zipName, + ); + await new Promise((resolve, reject) => { + compiler.outputFileSystem.mkdir( + path.dirname(zipOutputPath), + (err) => { + if (err) { + if (!isEEXIST(err)) { + reject(err); + } + } else { + compiler.outputFileSystem.writeFile( + zipOutputPath, + zipContent, + (writeErr) => { + if (writeErr) { + if (!isEEXIST(writeErr)) { + reject(writeErr); + } + } else { + resolve(); + } + }, + ); + } + }, + ); + }); + } + + if (apiTypesPath) { + const apiContent = fs.readFileSync(apiTypesPath); + const apiOutputPath = path.join( + compiler.outputPath, + zipPrefix, + apiFileName, + ); + await new Promise((resolve, reject) => { + compiler.outputFileSystem.mkdir( + path.dirname(apiOutputPath), + (err) => { + if (err) { + if (!isEEXIST(err)) { + reject(err); + } + } else { + compiler.outputFileSystem.writeFile( + apiOutputPath, + apiContent, + (writeErr) => { + if (writeErr) { + if (!isEEXIST(writeErr)) { + reject(writeErr); + } + } else { + resolve(); + } + }, + ); + } + }, + ); + }); + } + + callback(); + } + } catch (err) { + callback(); + if (finalOptions.displayErrorInTerminal) { + console.error('Error in mf:generateTypes processAssets hook:', err); + } + } + }; compiler.hooks.thisCompilation.tap('mf:generateTypes', (compilation) => { compilation.hooks.processAssets.tapPromise( @@ -99,71 +231,9 @@ export class GenerateTypesPlugin implements WebpackPluginInstance { compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER, }, async () => { - await consumeTypesPromise; - try { - if (pluginOptions.dev === false && compiledOnce) { - return; - } - - if (compiledOnce) { - // Dev types will be generated by DevPlugin, the archive filename usually is dist/.dev-server.zip - return; - } - - const { zipTypesPath, apiTypesPath, zipName, apiFileName } = - retrieveTypesAssetsInfo(finalOptions.remote); - if (zipName && compilation.getAsset(zipName)) { - return; - } - - await generateTypesFn(finalOptions); - const config = finalOptions.remote.moduleFederationConfig; - let zipPrefix = ''; - if ( - typeof config.manifest === 'object' && - config.manifest.filePath - ) { - zipPrefix = config.manifest.filePath; - } else if ( - typeof config.manifest === 'object' && - config.manifest.fileName - ) { - zipPrefix = path.dirname(config.manifest.fileName); - } else if (config.filename) { - zipPrefix = path.dirname(config.filename); - } - - const zipAssetName = path.join(zipPrefix, zipName); - if (zipTypesPath && !compilation.getAsset(zipAssetName)) { - compilation.emitAsset( - path.join(zipPrefix, zipName), - new compiler.webpack.sources.RawSource( - fs.readFileSync(zipTypesPath), - false, - ), - ); - } - - const apiAssetName = path.join(zipPrefix, apiFileName); - if (apiTypesPath && !compilation.getAsset(apiAssetName)) { - compilation.emitAsset( - path.join(zipPrefix, apiFileName), - new compiler.webpack.sources.RawSource( - fs.readFileSync(apiTypesPath), - false, - ), - ); - } - compiledOnce = true; - callback(); - } catch (err) { - callback(); - if (finalOptions.displayErrorInTerminal) { - console.error( - 'Error in mf:generateTypes processAssets hook:', - err, - ); - } + const emitTypesFilesPromise = emitTypesFiles(compilation); + if (isProd) { + await emitTypesFilesPromise; } }, );